Railsのテストについて知ってること

railsのテストについて、現状知っていることを書いていきます。
railsチュートリアル以来テストを書いていなかったので、railsチュートリアルに取り組んだ直後の方、取り組んでいる最中の方の役に立つかもしれません。

minitestを使うことを前提に書いていきます。

##テストの目的
機能を追加する度に、その機能が動作していることを保証するテストを書くようにすることで、
何か修正を加えた時に「これまで動いていた機能が動かなくなる」リスクを最小限に抑えることができます。

バグが減り、大規模な変更への怖さが減り、いちいちブラウザで手動で確認する手間が減ります。

テストを書く手間が発生しますが、テストを書くことに慣れてしまえば、その手間もあまり気にならなくなるかもしれません。

何ができるのか、何ができないのかを定義するので「仕様書」のような側面もあるみたいです。

##テストの種類
大きく分けて、**単体テスト**と**統合テスト**に分類されます。

単体テストは、各コントローラーやモデルの動作を検証します。

統合テストは「ログインして、投稿して、ログアウトして」のような、実際のユーザーの動きを再現するような形のテストで、複数のコントローラーやモデルを横断する動作を検証します。

##テストファイルの生成方法
テスト用ファイルは、コントローラー作成時等に自動生成されるものと、専用のコマンドを使って生成するものがあります。代表的なファイルの生成方法は以下の通りです。

**controller**用テストファイル：コントローラー作成時に自動生成
**model**用テストファイル、**fixture**：モデル作成時に自動生成
**mailer用テストファイル**：メイラー作成時に自動生成
**統合テスト**用ファイル：`$ g integration_test hogehoge`で生成

##テストの流れ
テストは、機能を追加する前に書いたり、機能を追加した直後に書いたりします。
テストを書かずに進めてしまうと、あとあとリファクタリングや機能の追加が怖くなります。

テストで利用するサンプルデータを`fixture`に書き、必要に応じて各テストファイル内で呼び出して使います。テストファイルの冒頭に`set up`という名前のメソッドを書いて、インスタンスを生成しておく方法がよく用いられます。

テスト内で使いたいメソッドは、`test_helper.rb`に定義します。

あとは各テストファイルにゴリゴリ書いていくのみです。

##どこにどんなテストを書くか
実際に、どのファイルにどんなテストを書いていくのかを探るべく、railsチュートリアルで書いたコードを全て読んで、ピックアップしてみました。

###fixture
各モデルに紐づいたサンプルデータを定義

###test_helper.rb
ログイン用メソッド×2、ログアウト用メソッド
→統合テストではsessionコントローラーにpostを投げてログインし、controllerやmodelの単体テストではsessionにidを直接保存する

###controller
・ログインしてないユーザーが投稿、削除した時のDBの変化とリダイレクトを確認
・他人のmicropostを削除した時のDBの変化とリダイレクトを確認
・ログインせずにフォロー、解除しようとした時の...
・ログイン画面が表示されるか確認
・固定ページが表示されるか確認
・ログインしてない時にユーザー情報変更画面に行けないことを確認
・ログインせずにユーザー情報を更新しようとした時、DBの値、flashメッセージの表示、リダイレクトを確認
・ログインしてユーザー情報を更新しようとした時、flashが空でリダイレクトされてることを確認
・Adminの値は書き換えられないことを確認
・未ログイン、管理者以外のログインではユーザーの削除ができないことを確認
・未ログインではフォロー、フォロワーの一覧ページにアクセスできないことを確認

###model
いろんな値を入れて`valid?`メソッドの戻り値を確認
レコードを削除した後の、DBのレコード数が減ったことを確認
フォロー後のフォローリストやフォロワーリストを確認
フォローしているユーザーの投稿が表示されているか確認

###mailer
メールのタイトル、宛先、本文内の記述などをチェック

###integration
(ログインした後の挙動)
フォロー一覧ページに、フォロー数分のユーザーページへのリンクがある
フォローしたらDBのレコード数が1つ増える
フォロー解除したらDBのレコード数が1つ減る
トップページにフィードに1ページ目の内容が表示されている

ログイン→ページ内の要素を確認→無効な投稿→有効な投稿→投稿を削除→違うユーザーのプロフィールにアクセスし、削除ボタンがないことを確認
投稿数が表示されていることを確認

無効なパスワードでログインし、flashとテンプレートを確認
パスワードリセットの流れを、いろんなエラーを発生させながら確認

指定のリンクが存在するか確認
いろんなページに移動して、ページタイトルを確認

ユーザー編集画面でエラーを発生させて、エラーメッセージの内容を確認
ユーザー編集を成功させて、登録した名前等が表示されているか確認

管理者としてログインして、ページネーションやリンクの数、deleteを押した時のDBのレコード数を確認
非管理者としてログインして、deleteボタンが無いことを確認

ログイン失敗時のレイアウトやflashの数等を確認
ログインしてログアウトする流れを確認
remember meの有無での挙動の違いを確認
プロフィールページに表示される内容を確認

サインアップ時にメールが1通送られたことを確認
有効化されてないユーザーなどでログインを試みた時の挙動を確認

##基本文法
```rb
test 'the truth' do
  assert hogehoge
end
```
`test 'テスト名' do`の下に、テストしたい内容を書いていきます。

```terminal
$ rails t
```

で作成した全てのテストを実行することができます。

##Assertionの内容
railsに標準で組み込まれている**minitest**では、`assert◯◯ ××`という形で、変数の値やDBのレコード数を確認していきます。

Railsチュートリアルに登場したassertionの一覧を以下にまとめました。[]内の数字は登場回数です。

####assert（assert_not）　[17 + 35]
testが真（nil、false以外）の場合にアサーションが成功します。

```rb
assert @user.valid?
assert_not flash.empty?
```

####assert_nil　[3]
obj.nil?が真の場合にアサーションに成功します。

```rb
assert_nil current_user
```

####assert_empty（assert_not_empty）　[1 + 1]
obj.empty?が真の場合にアサーションが成功します。

```rb
assert_empty cookies['remember_token']
assert_not_empty cookies['remember_token']
```

####assert_equal（assert_not_equal）　[16 + 1]
exp == actが真の場合にアサーションが成功します。

```rb
assert_equal @user, current_user
assert_not_equal @user.reset_digest, @user.reload.reset_digest
```

####assert_redirected_to　[25]
適切にリダイレクトされている場合にアサーションが成功します。

```rb
post ...
assert_redirected_to login_url
```

####assert_response　[8]
レスポンスが指定したステータスコードになっている場合にアサーションが成功します。

```
get root_path
assert_response :success
```

####assert_select　[38]
指定した種類、個数のHTMLの要素が存在する場合にアサーションが成功します。

```rb
assert_select 'div#error_explanation'
```

####assert_match　[19]
指定した正規表現に一致する場合、アサーションが成立します。特定の文字列が含まれているかどうか、の検証等でもよく利用されます。

```rb
assert_match @user.followers.count.to_s, response.body
```

####assert_template　[14]
指定されたテンプレート、レイアウトファイルが選択されている場合にアサーションが成功します。

```rb
assert_template 'users/edit'
```

####assert_difference（assert_no_difference）　[9 + 9]
create/update/deleteの実行前後のレコード数が指定通りに変化している場合にアサーションが成功します。ブロックで使います。

```rb
assert_difference '@user.following.count', 1 do
  post relationships_path, params:{
    followed_id: @other.id
  }
end
```







##Fixture
fixtureの使い方を簡単にまとめます。

定義と取り出し

```rb
# test/fixtures/hoges.ymlの内容
one:
  name: Hoge san
  email: hoge@hoge.com

# テストファイル内でレコードを取り出す方法
@one = hoges(:one)
@one.name #=> "Hoge san"
```
erb記法やyamlのエイリアスも使える

```yml
base: &minimal
  email: test_user@example.com
  name: テストユーザー
  payment_type: credit_card
  created_at: <%= Time.zone.now - 1.day %>
  updated_at: <%= Time.zone.now - 1.day %>

tarou:
  <<: *minimal
  email: test_tatou@example.com
  name: テスト太郎
```

・fixtureには、最小限のデータ、ありそうなデータ1,2個、エッジデータ（在庫0の商品を購入する、すでに完了したタスクを再度完了させるなど）を登録すると良い
・サンプルデータはfixtureを使わずに`User.new`などで新しいインスタンスを作成する方法もある

##その他
・`user.reload`で、DB登録後のレコードの内容をインスタンスに反映させることができる

##参考
[Rails チュートリアル　【初心者向け】　テストを10分でおさらいしよう！](https://qiita.com/duka/items/2d724ea2226984cb544f)
[Minitest でテスト、Rails のテスト (その1)](https://qiita.com/5t111111/items/5bb86896f85ac9cfc43f)
[Railsのテストの仕方（Minitest編）](https://qiita.com/troch/items/335a2dec9539307e65c6)