フォーマットを検証する
name属性の検証には、空文字でない、名前が51文字未満であるという最小限の制約しか与えていません
でした。
email属性の場合は、有効なメールアドレスかどうかを判定するために、もっと厳重な要求を満たさなければなりません
。
これまでは空のメールアドレスのみを禁止してきましたが、ここではメールアドレスにおなじみのパターン
user@example.comに合っているかどうかも確認することを要求します。
最初に、有効なメールアドレスと無効なメールアドレスのコレクションに対するテスト
を行いましょう。
このコレクションを作る方法として、次に示すように、文字列の配列を簡単に作れる%w[]
という便利なテクニックを知っておくと良いでしょう。
>> %w[foo bar baz]
=> ["foo", "bar", "baz"]
>> addresses = %w[USER@foo.COM THE_US-ER@foo.bar.org first.last@foo.jp]
=> ["USER@foo.COM", "THE_US-ER@foo.bar.org", "first.last@foo.jp"]
>> addresses.each do |address|
?> puts address
>> end
USER@foo.COM
THE_US-ER@foo.bar.org
first.last@foo.jp
=> ["USER@foo.COM", "THE_US-ER@foo.bar.org", "first.last@foo.jp"]
each
メソッドを使ってaddresses配列の各要素
を繰り返し取り出しました。
このテクニックを学んだことで、基本となるメールアドレスフォーマット検証
のテストを書く準備が整いました。
メールアドレスのバリデーションは扱いが難しく、エラーが発生しやすい部分なので、有効なメールアドレスと無効なメールアドレスをいくつか用意
して、バリデーション内のエラーを検知していきます。
具体的には、user@example,comのような無効なメールアドレスが弾かれることと、user@example.comのような有効なメールアドレスが通ることを確認しながら、バリデーションを実装していきます。
まずは、有効なメールアドレスを示します。
有効なメールフォーマットをテストする
require 'test_helper'
class UserTest < ActiveSupport::TestCase
def setup
@user = User.new(name: "Example User", email: "user@example.com")
end
test "should be valid" do
assert @user.valid?
end
test "name should be present" do
@user.name = ""
assert_not @user.valid?
end
test "email should be present" do
@user.email = " "
assert_not @user.valid?
end
test "name should not be too long" do
@user.name = "a" * 51
#51字以内
assert_not @user.valid?
end
test "email should not be too long" do
@user.email = "a" * 244 + "@example.com"
#244字以内
assert_not @user.valid?
end
test "email validation should accept valid addresses" do
valid_addresses = %w[user@example.com USER@foo.COM A_US-ER@foo.bar.org
first.last@foo.jp alice+bob@baz.cn]
#有効、無効のメールアドレスを用意
valid_addresses.each do |valid_address|
@user.email = valid_address
assert @user.valid?, "#{valid_address.inspect} should be valid"
#メールアドレスが有効ならば 以降の処理をする
end
end
end
assert @user.valid?, "#{valid_address.inspect} should be valid"
(詳細な文字列を調べるために4.3.3で紹介したinspectメソッドを使っています。)
どのメールアドレスで失敗したのかを知ることは非常に便利
です。
そこでは、eachメソッドを使って各メールアドレスを順にテストしています。
ループさせずにテストすると、失敗した行番号
とメールアドレスの行数
を照らし合わせて、失敗したメールアドレスを特定するといった作業が発生してしまいます。
次に、user@example,com(ドットではなくカンマになっている)やuser_at_foo.org(アットマーク ‘@’ がない)といった無効なメールアドレスを使って 「無効性(Invalidity)
」についてテストしていきます。
エラーメッセージをカスタマイズして、どのメールアドレスで失敗したのかすぐに特定できるようにしておきます。
メールフォーマットの検証に対するテスト
require 'test_helper'
class UserTest < ActiveSupport::TestCase
def setup
@user = User.new(name: "Example User", email: "user@example.com")
end
test "should be valid" do
assert @user.valid?
end
test "name should be present" do
@user.name = ""
assert_not @user.valid?
end
test "email should be present" do
@user.email = " "
assert_not @user.valid?
end
test "name should not be too long" do
@user.name = "a" * 51
#51字以内
assert_not @user.valid?
end
test "email should not be too long" do
@user.email = "a" * 244 + "@example.com"
#244字以内
assert_not @user.valid?
end
test "email validation should accept valid addresses" do
#有効なメールフォーマットを検証する
valid_addresses = %w[user@example.com USER@foo.COM A_US-ER@foo.bar.org
first.last@foo.jp alice+bob@baz.cn]
#有効、無効のメールアドレスを用意
valid_addresses.each do |valid_address|
@user.email = valid_address
assert @user.valid?, "#{valid_address.inspect} should be valid"
#メールアドレスが有効ならば 有効のメールアドレスを表示する
end
end
test "email validation should reject invalid addresses" do
#メールフォーマットの検証に対するテスト
invalid_addresses = %w[user@example,com user_at_foo.org user.name@example.
foo@bar_baz.com foo@bar+baz.com]
invalid_addresses.each do |invalid_address|
@user.email = invalid_address
assert_not @user.valid?, "#{invalid_address.inspect} should be invalid"
#有効でなければ 無効のメールアドレスを表示する
end
end
end
ubuntu:~/environment/sample_app (modeling-users) $ rails t
Running via Spring preloader in process 8260
Started with run options --seed 20507
FAIL["test_email_validation_should_reject_invalid_addresses", #<Minitest::Reporters::Suite:0x00007efe74ee67f8 @name="UserTest">, 0.03949682099982965]
test_email_validation_should_reject_invalid_addresses#UserTest (0.04s)
"user@example,com" should be invalid
test/models/user_test.rb:50:in `block (2 levels) in <class:UserTest>'
test/models/user_test.rb:48:in `each'
test/models/user_test.rb:48:in `block in <class:UserTest>'
15/15: [============================] 100% Time: 00:00:02, Time: 00:00:02
Finished in 2.77939s
15 tests, 30 assertions, 1 failures, 0 errors, 0 skips
test_email_validation_should_reject_invalid_addresses
がダメらしい。
メールアドレスのフォーマットを検証するためには、次のようにformatというオプションを使います。
validates :email, format: { with: /<regular expression>/ }
このオプションは引数に正規表現(Regular Expression)(regexとも呼ばれます)
を取ります。
正規表現は一見謎めいて見えますが、文字列のパターンマッチング
においては非常に強力な言語です。
つまり、有効なメールアドレスだけにマッチして、無効なメールアドレスにはマッチしない正規表現を組み立てる
必要があります。
本チュートリアルでは、実践的でシンプルな正規表現を採用します。これがその正規表現です。
VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i
正規表現を本当に理解するためには実際に使って見るのが一番です。
例えばRubular
という対話的に正規表現を試せるWebサイトがあります。
このWebサイトはインタラクティブ性に富んだインターフェイスを持っていて、また、正規表現のクイックリファレンスも兼ね備えています。Rubularをブラウザで開き、実際に試してみることを強くお勧めします。
正規表現は、読んで学ぶより対話的に学んだほうが早い
です。
正規表現を適用してemailのフォーマットを検証した結果を示します。
メールフォーマットを正規表現で検証する
class User < ApplicationRecord
validates :name, presence: true, length: { maximum: 50 }
#属性はname,属性の存在を検証、 最大50字まで
VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i
validates :email, presence: true, length: { maximum: 255 },
#最大255字まで
format: { with: VALID_EMAIL_REGEX }
end
正規表現VALID_EMAIL_REGEXは定数
です。
大文字で始まる名前はRubyでは定数
を意味します。次のコードは、
VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i
validates :email, presence: true, length: { maximum: 255 },
format: { with: VALID_EMAIL_REGEX }
これで、このパターンに一致するメールアドレスだけが有効であることをチェック
できるようになります。
ただ上の正規表現には、少しだけ残念な点があります。
それはfoo@bar..comのようなドットの連続を誤り
として検出できない、という点です。
正規表現を修正すると直りますが、これについては演習に持ち越すことにしましょう
(訳注: ドットの連続だけでなく、国際化ドメインにも対応できた方が良いでしょう。指摘してくれた@znzさん、ありがとうございます!)。
この時点では、テストは green になるはずです。
Started with run options --seed 41242
7/7: [==============================] 100% Time: 00:00:00, Time: 00:00:00
Finished in 0.03163s
7 tests, 15 assertions, 0 failures, 0 errors, 0 skips
演習
1.リスト 6.18にある有効なメールアドレスのリストと、リスト 6.19にある無効なメールアドレスのリストをRubularのYour test string:に転記してみてください。
lその後、リスト 6.21の正規表現をYour regular expression:に転記して、有効なメールアドレスのみがすべてマッチし、無効なメールアドレスはすべてマッチしないことを確認してみましょう。
2.先ほど触れたように、リスト 6.21のメールアドレスチェックする正規表現は、foo@bar..comのようにドットが連続した無効なメールアドレスを許容してしまいます。まずは、このメールアドレスをリスト 6.19の無効なメールアドレスリストに追加し、これによってテストが失敗することを確認してください。次に、リスト 6.23で示した、少し複雑な正規表現を使ってこのテストがパスすることを確認してください。
3.foo@bar..comをRubularのメールアドレスのリストに追加し、リスト 6.23の正規表現をRubularで使ってみてください。有効なメールアドレスのみがすべてマッチし、無効なメールアドレスはすべてマッチしないことを確認してみましょう。
1からNo matchesになり、できなかった。