Railsチュートリアル 第13章 ユーザーのマイクロポスト - Micropostモデルに、テスト駆動で新たな機能を追加していく

# 空文字列での投稿は受け付けない

## 「空文字列で投稿した場合、Micropostオブジェクトは有効ではない」というテストを追加する

「空文字列で投稿した場合、Micropostオブジェクトは有効ではない」とすれば、上述の機能は実現されます。対応するテストは、以下のようになります。名前は「content should be present」とします。

```ruby:
test "content should be present" do
  @micropost.content = "  "
  assert_not @micropost.valid?
end
```

上記テストを`test/models/micropost_test.rb`に追加します。

```diff:test/models/micropost_test.rb
  require 'test_helper'

  class MicropostTest < ActiveSupport::TestCase
    def setup
      @user = users(:rhakurei)
      #HACK: このコードは慣習的に正しくないため、要修正
      @micropost = Micropost.new(content: "Lorem ipsum", user_id: @user.id)
    end

    test "should be valid" do
      assert @micropost.valid?
    end

    test "user id should be present" do
      @micropost.user_id = nil
      assert_not @micropost.valid?
    end
+
+   test "content should be present" do
+     @micropost.content = "  "
+     assert_not @micropost.valid?
+   end
  end
```

## 「空文字列で投稿した場合、Micropostオブジェクトは有効ではない」テストを追加した時点でのテストの結果

```
# rails test test/models/micropost_test.rb
Running via Spring preloader in process 1355
Started with run options --seed 47132

 FAIL["test_content_should_be_present", MicropostTest, 0.2884534999902826]
 test_content_should_be_present#MicropostTest (0.29s)
        Expected true to be nil or false
        test/models/micropost_test.rb:21:in `block in <class:MicropostTest>'

  3/3: [===================================] 100% Time: 00:00:00, Time: 00:00:00

Finished in 0.79459s
3 tests, 3 assertions, 1 failures, 0 errors, 0 skips
```

`test/models/micropost_test.rb`に対するテストは、上記のような結果を返して失敗します。

私の環境では、`test/models/micropost_test.rb`の20行目から21行目は以下のようになっています。

```ruby:test/models/micropost_test.rb（20〜21行目）
@micropost.content = "  "
assert_not @micropost.valid?
```

「空のマイクロポストを受け付けてしまっている」という失敗ですね。想定通りのテストが実装できているといえます。

## 「空文字列での投稿は受け付けない」という機能の追加

Micropostモデルに、以下のようなバリデーションを追加すればOKです。「マイクロポストには内容が存在している必要がある」というバリデーションですね。

```ruby:
validates :content, presence: true
```

```diff:app/models/micropost.rb
  class Micropost < ApplicationRecord
    belongs_to :user
+   validates :content, presence: true
  end
```

## 「空文字列で投稿した場合、Micropostオブジェクトは有効ではない」テストが成功することを確認する

上記実装を追加した時点で、改めて`test/models/micropost_test.rb`に対するテストを実行してみます。

```
# rails test test/models/micropost_test.rb
Running via Spring preloader in process 1368
Started with run options --seed 43879

  3/3: [===================================] 100% Time: 00:00:00, Time: 00:00:00

Finished in 0.79976s
3 tests, 3 assertions, 0 failures, 0 errors, 0 skips
```

今度は成功しました。「空文字列での投稿は受け付けない」という機能を正しく追加できたといえますね。

# マイクロポストの長さを140文字以内に制限する

「マイクロポストの文字数が141文字以上の場合、Micropostオブジェクトは有効ではない」とすれば、上述の機能は実現されます。対応するテストは、以下のようになります。名前は「content should be at most 140 characters」とします。

```ruby:
test "content should be at most 140 characters" do
  @micropost.content = "a" * 141
  assert_not @micropost.valid?
end
```

上記テストを`test/models/micropost_test.rb`に追加します。

```diff:test/models/micropost_test.rb
  require 'test_helper'

  class MicropostTest < ActiveSupport::TestCase
    def setup
      @user = users(:rhakurei)
      #HACK: このコードは慣習的に正しくないため、要修正
      @micropost = Micropost.new(content: "Lorem ipsum", user_id: @user.id)
    end

    test "should be valid" do
      assert @micropost.valid?
    end

    test "user id should be present" do
      @micropost.user_id = nil
      assert_not @micropost.valid?
    end

    test "content should be present" do
      @micropost.content = "  "
      assert_not @micropost.valid?
    end
+
+   test "content should be at most 140 characters" do
+     @micropost.content = "a" * 141
+     assert_not @micropost.valid?
+   end
  end
```

### 141文字の文字列を手っ取り早く生成する方法

（復習）Rubyにおいて141文字の文字列を生成するためには、文字列の乗算を使う方法が手っ取り早いです。

```
# rails console
>> "a" * 141
=> "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
```

## 「マイクロポストの文字数が141文字以上の場合、Micropostオブジェクトは有効ではない」テストを追加した時点でのテストの結果

```
# rails test test/models/micropost_test.rb
Running via Spring preloader in process 1381
Started with run options --seed 7585

 FAIL["test_content_should_be_at_most_140_characters", MicropostTest, 0.22907639999175444]
 test_content_should_be_at_most_140_characters#MicropostTest (0.23s)
        Expected true to be nil or false
        test/models/micropost_test.rb:26:in `block in <class:MicropostTest>'

  4/4: [===================================] 100% Time: 00:00:00, Time: 00:00:00

Finished in 0.72279s
4 tests, 4 assertions, 1 failures, 0 errors, 0 skips
```

`test/models/micropost_test.rb`に対するテストは、上記のような結果を返して失敗します。

私の環境では、`test/models/micropost_test.rb`の25行目から26行目は以下のようになっています。

```ruby:test/models/micropost_test.rb（25〜26行目）
@micropost.content = "a" * 141
assert_not @micropost.valid?
```

「141文字のマイクロポストを受け付けてしまっている」という失敗ですね。想定通りのテストが実装できているといえます。

## 「マイクロポストで受け付ける文字数は140文字まで」という機能の追加

Micropostモデルのバリデーションを、以下のように変更すればOKです。「マイクロポストには内容が存在しており、かつ文字数は140以下である必要がある」というバリデーションですね。

```diff:app/models/micropost.rb
  class Micropost < ApplicationRecord
    belongs_to :user
-   validates :content, presence: true
+   validates :content, presence: true, length: { maximum: 140 }
  end
```

## 「マイクロポストの文字数が141文字以上の場合、Micropostオブジェクトは有効ではない」テストが成功することを確認する

上記実装を追加した時点で、改めて`test/models/micropost_test.rb`に対するテストを実行してみます。

```
# rails test test/models/micropost_test.rb
Running via Spring preloader in process 1402
Started with run options --seed 39026

  4/4: [===================================] 100% Time: 00:00:00, Time: 00:00:00

Finished in 0.77221s
4 tests, 4 assertions, 0 failures, 0 errors, 0 skips
```

今度は成功しました。「マイクロポストで受け付ける文字数は140文字まで」という機能を正しく追加できたといえますね。