Ruby on Rails初心者向け　テストについて

#はじめに
初心者の気持ちがわかるのは初心者のうちだけ
ということで、Rails勉強中プログラミング初心者の自分がRailsのテストについて、なぜテストを行うのか？の段階から実装までをまとめてみました。
この記事は、Progate等で一通り知識を付けこれから何か成果物づくりに励もうとしている方など、駆け出しの方を対象としています。

#対象読者
・Rails勉強中の初心者 


#テストの必要性
プログラミングにおけるテストの存在意義については以下のものがあります。

・コードの動作確認の効率化
　-リリースするたびに全てのコードを手作業で確認するのは効率が悪い。
・バグ発生時の時間短縮
　-テストを普段から行っていれば、発生したバグの原因を絞り込みやすい。
・コード修正中のリスク軽減、セーフティネット
  　-ある部分のコード修正により影響を受けた他のコードが動かなくなるという事態の防止

#テスティングフレームワーク
RailsのにはRSpec,minitest,test-unitなどいくつかのテスティングフレームワークがありますが、今回はデフォルトでRubyにインストールされていてよりシンプルなminitestを使っていきます。

#テストの種類
テストは主に以下の2種類があります。

|   テスト名                |　                          内容　　　　　              　|
|:----------------------:|:-------------------------------------------------|
|単体テスト               |                        クラスやメソッド単位での最もシンプルなテスト |
|統合テスト             |複数のコントローラーやメソッドにまたがり、実際にユーザーが行う一連の操作が正常に実行できるかチェックするテスト|
#準備

最初にテスト練習用プロジェクトとUserモデルを作成しましょう。
テスト練習用プロジェクト作成

```
$ rails new test_app
```
次に今作成したプロジェクト内に移動しnameとemail二つのカラムを持つUserモデル作成

```
$ rails generate model User name:string email:string
```
次のコマンドも忘れずに

```
$ rails db:migrate
```
これで準備は整いました。まずは基本的な単体テストから行います。
それではさっそくtest_app/test/models/user_test.rbに記述していきましょう。


#単体テスト
基本的なテストで、主にモデルやビューのテストを行います。


```ruby:test/models/user_test.rb
 require 'test_helper'

class UserTest < ActiveSupport::TestCase

  def setup
    @user = User.new(
      name:"example",
      email:"example"
    )
  end

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

先頭の`require 'test_helper'`によってtest/test_helper.rbに記載されているデフォルト設定を読み込みます。今後書くすべてのテストにもこれを記述するので、テスト全体で使いたいメソッドはこのtest_helper.rbに記述するようにします。

```ruby:test/models/user_test.rb
 require 'test_helper'

class UserTest < ActiveSupport::TestCase

  def setup
    @user = User.new(
      name:"example",
      email:"example"
    )
  end

 ...

end
```
setup関数では、テストに先立ちnewメソッドでUserオブジェクトを作成します。

```ruby:test/models/user_test.rb
 require 'test_helper'

class UserTest < ActiveSupport::TestCase

  ...

  test "should be valid" do
     assert @user.valid?
  end
end
```
上記がUserモデルに対するtestメソッドで、`test "should be valid" do` は `def test_should_be_valid`と解釈されます。
assertメソッドでは、第一引数が真である場合にテストが成功したとみなされます。この場合Userオブジェクトである@user変数はvalid?メソッドに真を返すので、上記テストは成功となります。

```
$ rails test
Running via Spring preloader in process 8174
Run options: --seed 13701

# Running:

.

Finished in 0.189369s, 5.2807 runs/s, 5.2807 assertions/s.
1 runs, 1 assertions, 0 failures, 0 errors, 0 skips
```
0 failures, 0 errors となっているので、テスト成功です。
assertメソッドにはそれ以外にも、assert_not, assert_equal, assert_matchなどがありますが、今回は割愛します。
#テストを用いた開発手法
さて、とりあえず上記の手法でテストは成功させることができましたが、では実際の開発ではどのようなサイクルでテストを行うのか。
その一つにテスト駆動開発(TDD)というものがあります。
流れは以下の通り

　・失敗するテストを書く
　・できる限り早く、テストに通るような最小限のコードを書く
　・コードの重複を除去する（リファクタリング）

それでは次の統合テストではテスト駆動開発を用いてコーディングを行っていきます。

#統合テスト
統合テストでは、実際のユーザーの操作を想定しテストを作っていきます。
今回はユーザーがトップページにアクセスした場合を想定します。

　・トップページにGETリクエストを送る
　・リクエストしたテンプレートが正しく表示されるかどうか確かめる
　・トップページから他ページへのリンクが正しく動作するかどうか確かめる

以上のステップでテストを行います。
それではまず統合(Integration)テストファイルを作成しましょう。

```
$ rails generate integration_test site_layout
```
このコマンドでtest_app/test/integration下に作成されたsite_layout_test.rbに以下のコードを加えてください。

```ruby:test/integration/site_layout_test.rb
require 'test_helper'

class SiteLayoutTest < ActionDispatch::IntegrationTest

  test "layout links" do
    get home_top_path
    assert_template 'home/top'
    assert_select "a[href=?]", home_about_path
  end
end

```
`get home_top_path`で、トップページへのGETメソッドを送り、`assert_template 'home/top'`が、指定したテンプレートが表示されたかどうかを確かめています。そして、`assert_select "a[href=?]", home_about_path`はトップページ上からaboutページへのリンクが正しく動作しているかの確認です。
それでは`rails test`コマンドでテストを行ってみましょう。まだ何の機能も付いていないので、当然テストは失敗します。
赤文字でエラーが表示されていれば、現段階の目的は達成です。
次はテストを成功させるために必要な機能を実際に搭載していきましょう。
まずは、コントローラーと必要なアクションを作成します。

```
$ rails generate controller home top about
```
そして今作成されたapp/views/home/top.html.erbを以下のようにしてください。

```
<h1>Home#top</h1>
<p>Find me in app/views/home/top.html.erb</p>
<%= link_to("about",("/home/about")) %>
```
最後の一文がaboutページへのリンクを表しています。また`rails generate controller`で、作成されたトップページへのパスも自動的に割り振られているのでテストは成功するはずです。

```
$ rails test
Running via Spring preloader in process 8700
Run options: --seed 28150

# Running:

....

Finished in 5.034895s, 0.7945 runs/s, 0.9931 assertions/s.
4 runs, 5 assertions, 0 failures, 0 errors, 0 skips
```
上記のように表示されればテスト成功です。
ここからはコードに重複があれば除去(リファクタリング)し、数多くあるassertメソッドを駆使しながら、この工程を繰り返し開発を行っていくことになります。
何らかの変更が上記の機能に影響を及ぼしバグを発生させても、テストを行った時点でそれに気づくことができるので、何がバグを引き起こしているのかわからない、といった事態も避けることができます。

#最後に
今回はテストについてまとめさせていただきました。
一人でも多くの駆け出しエンジニア達に有益な情報と受け取っていただければ幸いです。