通らなかったテスト
RSpec でテストを学び始めたが、
it "nicknameが存在しなければ無効な状態であること" do
user = User.new(nickname: nil)
user.valid?
expect(user.errors[:nickname]).to include("can't be blank")
end
このコードで、なぜ
user.valid?
の部分が必要になるのかわからなかった。ただ単に expect()メソッドに引数を渡してあげればいいのでは?と思い user.valid?を省略したらテストが通らなかったのでその理由を調査。
valid?メソッド
そもそも valid?メソッドは Rails ガイドによると
Rails は、Active Record オブジェクトを保存する直前にバリデーションを実行します。バリデーションで何らかのエラーが発生すると、オブジェクトを保存しません。
valid?メソッドを使って、バリデーションを手動でトリガすることもできます。valid?を実行するとバリデーションがトリガされ、オブジェクトにエラーがない場合は true を返し、エラーの場合は false を返します。
手動でバリデーションを行い、true/false を返してくれるものらしい。
Active Record でバリデーションが行われた後で errors インスタンスメソッドを使うと、失敗したバリデーションにアクセスできます。このメソッドはエラーのコレクションを返します。 その名の通り、バリデーション実行後にコレクションが空である場合はオブジェクトは有効です。
逆にいうとバリデーションが行われた後でないと失敗したバリデーションにアクセスできないということか。
ただし、new でインスタンス化されたオブジェクトは、それが厳密には無効であってもエラーは出力されないので、注意が必要です。これは、create や save メソッドなどによってオブジェクトが保存されるときのみ、バリデーションが自動的に実行されるためです。
new のインスタンス化ではエラーは出力されないと。だからエラーを出力するために手動で valid?メソッドを行う必要がある。
errors[]
errors はどのようなメソッドなのか。これも Rails ガイドによると
errors[:attribute]を使うと、特定のオブジェクトの属性が有効かどうかを確認できます。このメソッドは、:attribute のすべてのエラーの配列を返します。指定された属性でエラーが発生しなかった場合は、空の配列が返されます。
このメソッドが役に立つのは、バリデーションを実行した後だけです。このメソッドはエラーのコレクションを調べるだけで、バリデーションそのものをトリガしないからです。
まとめ
つまり、errors 自体はバリデーションをトリガーしないため、user.valid?を行わないとエラーが出力されない。そのため、バリデーション前に errors[:nickname]は空配列となってしまい、テストが通らない。