【Rails】assertとassert_predicateの違い

## 1. はじめに

現在、RailsチュートリアルでRuby on Railsを学習しています。

第6章で紹介されるテストコードに対してRubocopで解析をかけたところ、`assert @user.valid?`ではなく`assert_predicate(@user, :valid?)`の使用を推奨されました。

今回はこれら2つの違いについてまとめていきます。

## 2. assert・assert_predicateとは

assertメソッドもassert_predicateメソッドもMinitestで使えるアサーションの1つです。
[Rails テスティングガイド](https://railsguides.jp/testing.html)の説明を引用してまとめると以下の通りです。



**■ assert**
指定した式(test)がtrueである場合、テスト成功。

> **`assert( test, [msg] )`**
> 
> - `test`はtrueであると主張する。


<br>

**■ asset_predicate**
オブジェクト(obj)の述語メソッド(predicate)の結果がtrueである場合、テスト成功。

> **`assert_predicate ( obj, predicate, [msg] )`**
> 
> - `obj.predicate`はtrueであると主張する (例:`assert_predicate str, :empty?`)。

<br>

`assert_predicate(@user, :valid?)`は`@user.valid?`がtrueの場合に成功とも言い換えることができます。そのため、`assert @user.valid?`と`assert_predicate(@user, :valid?)`は同じ内容をテストしていると言えます。

## 3. assert・assert_predicateの比較

次に、テストコードを実際に実行させ、それぞれのアサーションで出力結果にどのような違いがあるのかを比較していきます。

### 3.1 実行内容

name属性が空白の場合にエラーとなるバリデーションを設定し、実行結果を比較します。


```ruby:user.rb
class User < ApplicationRecord
  validates :name, presence: true
end
```

### 3.2 実行結果（成功）

作成するインスタンスのname属性に対し、"Example User”を与えテストを成功させます。

```ruby:user_test.rb
require "test_helper"

class UserTest < ActiveSupport::TestCase

	# name属性に"Example User"を与え、テストを成功させる。
  def setup
    @user = User.new(name: "Example User", email: "user@example.com")
  end

  test "should be vaild with assert" do
    assert @user.valid?
  end

  test "should be vaild with assert predicate" do
    assert_predicate(@user, :valid?)
  end
end
```

`rails test test/models/user_test.rb`の実行結果です。

```plaintext:terminal
$ rails test test/models/user_test.rb 
Running 2 tests in a single process (parallelization threshold is 50)
Started with run options --seed 9459

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

Finished in 0.05677s
2 tests, 2 assertions, 0 failures, 0 errors, 0 skips
```

想定通り、どちらのテストも成功し上記の出力結果が得られました。

### 3.3 実行結果（失敗）

作成するインスタンスのname属性に対し、空白を与えテストを失敗させます。

```ruby:user_test.rb
require "test_helper"

class UserTest < ActiveSupport::TestCase

	# name属性に空白を与え、テストを失敗させる。
  def setup
    @user = User.new(name: "　", email: "user@example.com")
  end

  test "should be vaild with assert" do
    assert @user.valid?
  end

  test "should be vaild with assert predicate" do
    assert_predicate(@user, :valid?)
  end
end
```

`rails test test/models/user_test.rb`の実行結果です。

```plaintext:terminal
$ rails test test/models/user_test.rb 
Running 2 tests in a single process (parallelization threshold is 50)
Started with run options --seed 40102

 FAIL UserTest#test_should_be_vaild_with_assert_predicate (0.10s)
        Expected #<User id: nil, name: " ", email: "user@example.com", created_at: nil, updated_at: nil> to be valid?.
        test/models/user_test.rb:13:in `block in <class:UserTest>'

 FAIL UserTest#test_should_be_vaild_with_assert (0.10s)
        Expected false to be truthy.
        test/models/user_test.rb:9:in `block in <class:UserTest>'

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

Finished in 0.10406s
2 tests, 2 assertions, 2 failures, 0 errors, 0 skips
```

想定通りどちらのテストも失敗となりましたが、異なるエラーメッセージが表示されました。エラーメッセージの箇所を抜粋すると、以下の通りです。

**■ assert**

```
Expected false to be truthy.
```

**■ assert_predicate**

```
Expected #<User id: nil, name: " ", email: "user@example.com", created_at: nil, updated_at: nil> to be valid?.
```

assertの場合、trueを期待しているのに対し、falseであるという決められたエラーメッセージが出力される。

assert_predicateの場合、指定したオブジェクトの属性情報と、`to be valid?`のようにテスト内容(述語メソッド)が出力される。

## 4. まとめ

assert_predicateメソッドの方が、テストが失敗したときにより具体的なエラーメッセージが提供されるため、何が間違っていたのかを理解しやすくなると感じました。

テスト結果は変わらないため、個人の好みやチームのコーディングルールによって決めるぐらいで良いかと思います。

## 5. 参考文献
- [Rails テスティングガイド - Railsガイド](https://railsguides.jp/testing.html)
- [第6章 ユーザーのモデルを作成する - Railsチュートリアル](https://railstutorial.jp/chapters/modeling_users?version=7.0#cha-modeling_users)