読者です 読者をやめる 読者になる 読者になる

tyoshikawa1106のブログ

- Force.com Developer Blog -

Rails:ユーザーの検証について

Railsチュートリアルのユーザの検証についてやりました。

最初のユーザーテスト

サンプルアプリケーションの他の機能と同様、Userモデルへの検証の追加もテスト駆動開発 (TDD) で行います。モデル作成時に、ユーザーをテストするための初期specも同時に生成しています。

f:id:tyoshikawa1106:20150803123608p:plain


上のコードではpendingメソッドだけが置かれており、何か意味のあるコードでspecを埋めるように促しています。このコードの効果は、空のテスト用データベースを用意してUserモデルのspecを実行することで確認できます。

$ bundle exec rake db:migrate
$ bundle exec rake db:test:prepare
$ bundle exec rspec spec/models/user_spec.rb

f:id:tyoshikawa1106:20150803123906p:plain

多くのシステムでは、pendingのspecはコマンドライン上で黄色で表示されます。黄色は、成功 (緑) と失敗 (赤) の中間を意味します。


次のコマンドは開発データベースのデータモデルdb/development.sqlite3がテストデータベースdb/test.sqlite3に反映されるようにするものです。

$ bundle exec rake db:test:prepare


マイグレーションの後でたまにRakeタスクが実行できなくなることがあるみたいです。テストデータベースはたまに壊れることがあるので、その場合はリセットが必要です。もしテストスイートが理由もなく壊れるようなことがあれば、rake db:test:prepareを実行して、この問題が解決するか確認すればいいみたいです。


":nameと:email"属性をRSpecの例に合わせて変更してテストします。
f:id:tyoshikawa1106:20150803124402p:plain


beforeブロックは前処理用で、各サンプルが実行される前にそのブロックの中のコードを実行します。この場合、User.newと初期化用の有効なハッシュを使って、新しい@userインスタンス変数を作成します。


@userをテストサンプルのデフォルトのsubjectとして設定します。

subject { @user }


name属性とemail属性の存在をテストします。

it { should respond_to(:name) }
it { should respond_to(:email) }


Userオブジェクトがname属性を持っていない場合、beforeブロックの中で例外を投げるので、一見、これらのテストが冗長に思えるかもしれません。しかし、これらのテストを追加することで、user.nameやuser.emailが正しく動作することを保証できます (beforeブロック内では、ただ属性のハッシュをnewに渡せるかどうかをテストしているだけです)。


respond_toメソッドは、Rubyのrespond_to?メソッドを暗黙的に使っています。respond_to?メソッドは、シンボルを1つ引数として受け取り、そのシンボルが表すメソッドまたは属性に対して、オブジェクトが応答する場合はtrueを返し、応答しない場合はfalseを返します。
f:id:tyoshikawa1106:20150803124626p:plain

(Rubyではtrue/falseの真偽値を返すメソッド名の末尾に?記号を置く慣習があります。)

以下のコードは
f:id:tyoshikawa1106:20150803125232p:plain


以下のコードでテストができます。
f:id:tyoshikawa1106:20150803125253p:plain


ここでは、subject { @user }と記述してあるので、@userを使わずに以下のように書くことができます。
f:id:tyoshikawa1106:20150803125329p:plain



このようなテストが行えるので、新しい属性やメソッドをUserモデルに一時的に追加してテスト駆動開発を行うことができます。さらに、すべてのUserオブジェクトがこれらのメソッドに応答する必要があるという仕様もここで明らかになりました。

rake db:test:prepareを実行してテスト環境用データベースを用意したので、テストはパスするはずです。

$ bundle exec rspec spec/

f:id:tyoshikawa1106:20150803124820p:plain


name属性とemail属性の存在性を検証したい場合は次のようになります。
f:id:tyoshikawa1106:20150803130458p:plain

プレゼンスを検証する

プレゼンス (存在性)は与えられた属性が存在することを検証します。たとえばこの節では、ユーザーがデータベースに保存される前にnameとemailフィールドの両方が存在することを保証します。

name属性の存在を検証する。

f:id:tyoshikawa1106:20150803125533p:plain


上記コードは次のようにも書けます。
f:id:tyoshikawa1106:20150803125603p:plain


とりあえずテストはこんな感じになります。
f:id:tyoshikawa1106:20150803130744p:plain

f:id:tyoshikawa1106:20150803130841p:plain

長さを検証する

文字数を検証するときは次のように書きます。

before { @user.name = "a" * 51 }

f:id:tyoshikawa1106:20150803131156p:plain


テストを通るようにするには:maximumパラメータと共に用いられる:lengthで長さの上限を強制します。
f:id:tyoshikawa1106:20150803131252p:plain


ここまでが、存在テストと長さのテストになります。

フォーマットを検証する

続いてフォーマットの検証を行います。

文字列の配列を簡単に作れるテクニック

%wを使って簡単に配列を用意できます。
f:id:tyoshikawa1106:20150803142352p:plain


メールアドレスフォーマットの検証テストの方法です。(spec/models/user_spec.rb)
f:id:tyoshikawa1106:20150803142610p:plain


上のテストはすべてを盛り込んだものではありませんが、一般的に有効なメールアドレスの形式であるuser@foo.COM、THE_US-ER@foo.bar.org (大文字、アンダースコア、複合ドメイン) 、first.last@foo.jp (一般的な企業のユーザー名「名.姓」と、2文字のトップレベルドメイン「jp」) を、いくつかの無効な形式と共に確認します。


メールアドレスのフォーマット検証を行うアプリケーションコードでは、validatesメソッドの:format引数に、フォーマットを定義するための正規表現 (regular expression) (regexとも書きます) を与えます


正規表現を使ったメールアドレスフォーマットの検証。(app/models/user.rb)
f:id:tyoshikawa1106:20150803143026p:plain

正規表現VALID_EMAIL_REGEXは定数です。大文字で始まる名前はRubyでは定数を意味します。

正規表現の例

f:id:tyoshikawa1106:20150803143220p:plain

一意性の検証

重複するメールアドレスの拒否のテスト。(spec/models/user_spec.rb)
f:id:tyoshikawa1106:20150803144433p:plain


上のコードは、@userと同じメールアドレスのユーザーを事前に作成する手法です。今回は、同じ属性のユーザーを作るために、@user.dupを使っています。同じ属性のユーザーが保存された後では、元の@userと同じメールアドレスが既にデータベース内に存在しているため、@userは無効になります。


メールアドレスの一意性の検査。(app/models/user.rb)
f:id:tyoshikawa1106:20150803144525p:plain


通常、メールアドレスでは大文字小文字が区別されません。すなわち、foo@bar.comはFOO@BAR.COMやFoO@BAr.coMと書いても扱いは同じです。従って、メールアドレスの検証ではこのような場合も考慮する必要があります。


大文字小文字を区別しない、重複するメールアドレスの拒否のテスト。
f:id:tyoshikawa1106:20150803144748p:plain


上のコードではStringのupcaseメソッドを使っています 。このテストは最初のメールアドレスの重複テストと同じことをしていますが、大文字に変換したメールアドレスを使っている点が異なります。


メールアドレスの大文字小文字を無視した一意性の検証(app/models/user.rb)
f:id:tyoshikawa1106:20150803145305p:plain


Railsはこの書き方で:uniquenessをtrueと判断します。


email属性を小文字に変換してメールアドレスの一意性を保証する方法(app/models/user.rb)
f:id:tyoshikawa1106:20150803171057p:plain