質問: "a" * 244 + "@example.com" assert_not @user.valid? end end ``` 上記をテストするとREDとなります。 ``` $ rails test ``` テストをパスするために、以下のコードを app/models/user.rb に追記します。 ``` class User < ApplicationRecord validates :name, presence: true, length: { maximum: 50 } validates :email, presence: true, length: { maximum: 255 } end ``` 再びテストすると、GREENとなります。 ``` $ rails test ``` ここまでのテストコードを踏まえて回答をいただきたいのは、以下の2点です。 1. **なぜ失敗するコードを最初に書かなければいけないのでしょうか?** 2. **なぜ、user_test.rbのコードは失敗になるのでしょうか?** **(おそらく、 assert_not @user.valid? の意味が理解できていません)** まだまだ初心者の域を出ていないので、なるべく噛み砕いてわかりやすく文章にしていただけると助かります。 どうか皆様にお力添えいただけますと幸いです。よろしくお願いします。
回答: > > 1. なぜ失敗するコードを最初に書かなければいけないのでしょうか? > > > 初心者の場合、テストの意義を真の意味で理解するのは無理です。 馬鹿にしているわけではないので怒らないのでほしいのですが、 テスト駆動開発というのは、 何らかの仕様があって、それを実装できるのは当たり前の状態の人を対象としてます。 RailsやRuby、Webアプリケーションの開発経験が十分にない状態では、コードの品質について考える事はなかなかないでしょう。 テストはコードの品質を高める(リファクタリング)、仕様を動くコードとして表現するドキュメントとしての役割もあります。 それから、別に失敗するテストを書いているわけではない点にも注意してください。 結果として失敗するだけです。ここが重要です。 まず何かを実装するという場合、仕様が先にあります。 ある入力に対して、出力がどうなるのが正しいのか?という事を定義するのが仕様です。 それをそのままテストとして書きますが、この段階では、その仕様は実装されていませんよね。 実装の前にテストを書くので、メソッドがなかったり、新しい仕様に対して現在のコードが対応していないので、テストが失敗するというわけです。 わざわざ失敗するテストを書くという考え方ではなくて、 まずこういう仕様を今から作るぞと自分の頭を整理するためにテストを書きます。 そして、正しく実装できたら、このテストデータに対して、こういう結果になるはずという事を記述するのがテストです。 初心者のうちは、いきなり実装を進めて行って、あれ?うまく行かないぞ?となって、作り直しをたくさんします。 中級者くらいになると、いきなり手を動かさずにじっくり考えて整理してから実装すると手戻りが少ないという事を経験として学びます。 その手段の1つとして、テストを先に書くという方法があるわけです。 正しくテストを記述できれば、すべてのテストが合格するように実装を進めればいいだけなので、試行錯誤するにしても手戻りが少なくて済みます。 これも経験則でそう分かるようになるのですが、プログラミング自体が初心者だったりすると、これは頭では理解しても、腑に落ちないでしょう。 > > 2. なぜ、user_test.rbのコードは失敗になるのでしょうか? > > > さきほど回答した内容に即していくと、 (1) 仕様を先に決める(頭の中だけでもいいし、紙に書いたりしてもいい) (2) 仕様をテストとして記述する (3) 仕様を満たすために実装する まず、Userモデルですが、バリデーションがない状態とします。 ``` class User < ApplicationRecord end ``` この状態の時に、 (1)仕様を決める ・nameの長さは50文字以内 ・emailの長さは255文字内 という仕様を実装者=あなたが考えたというシチュエーションをRailsチュートリアルではやっているものと思われます。 というか、そうしないとテストの内容と整合性がないわけですから。 (2)仕様をテストとして記述する ちょっとRailsチュートリアルのテスト説明がおかしいというかふさわしくないので、日本語で書かせてもらいます。 それから、チュートリアルのコードをそのまま先頭から順番に写していますか? 一般的にプログラムというのは、そういう風に書きませんので、注意が必要です。 私ならば、まずこのように大枠だけ書きます。 ちなみに、assert_notは引数の結果がfalseの時にテストが成功とする命令です。 ここではバリデーションが失敗する@user.valid?の結果がfalseとなる事を期待するテストを書くつもりというわけです。 ``` class UserTest < ActiveSupport::TestCase ... test "nameの長さは50文字以内である事" do # 50文字を超えたらエラーになる end test "emailの長さは255文字以内である事" do # 255文字を超えたらエラーになる end end ``` これで仕様を定義できました。 次にテストとして成立するように追記します。 さきほどの「~文字を超えたらエラーになる」はToDOメモというかそういう風にテストを書くよという意味のメモなので、テストを実際に書くときには消します。 ``` class UserTest < ActiveSupport::TestCase ... test "nameの長さは50文字以内である事" do # エラーを起こすために、わざと51文字を設定する @user.name = "a" * 51 # もし、正しくバリデーションが設定されていたら、 # バリデーションがfalseになるはず # assert_notは引数の結果がfalseに時にテストが成功する assert_not @user.valid? end test "emailの長さは255文字以内である事" do # エラーを起こすために、わざと255文字を超える文字列を設定 # "a" * 256だけでもいいでしょう @user.email = "a" * 244 + "@example.com" assert_not @user.valid? end end ``` この状態でテストを実行すると、当然失敗するはずなんですね。 Userにバリデーションを書いていないから、@user.valid? がtrueを返すため、 assert_not true となり、テスト失敗。 まずは、これを確認する事で、今のコードの状態が正しいと認識できます。 これでテストが一部でも成功してしまうと、自分の認識と現状のコードの状態がずれているという事になり、どこかにミスがあるという事が発見できるわけです。 (3) 仕様を満たすために実装する テストを書く事によって、ゴールまでの道筋が見えました。 あとは、テストが通るように実装するだけです。 この例では、バリデーションを追加するだけなので、説明不要ですね。