後へ      Topへ      次へ

自動テスト: 全般の解説

Rails は自動テストの環境が充実しており、
CI (Continuous Integration) に向いています。

rails generate の多くでテスト用のテンプレートが生成されます。
テンプレートの作法に則ってテストを書いていくとラクです。

通常の開発であれば、
コード変更とテストコード作成はセットです。
コードを書いたら即テストして、デバッグ。
自動テストでほぼカバーできる Rails は、
デグレード・チェックも含めて時間効率が優れています。

が、
サンプルプログラムはコードの一例として書いているだけで、
テストコードの実体は殆どありません。
ご容赦ください。

 

fixtures

\test\fixtures には、
テストで使用する、各モデル毎のデフォルト値を YAML で書きます。

例えば、

one:
  enabled: true
  name: こころ
  isbn: "978-4101010014"
  category: one
  step: one
  memo: MyString

のようにです。
各テスト実施毎にデータベースがこのデフォルト値になります。

YAML 上、references のカラムは、
ID の数値を直書きせずとも、参照先の YAML のラベルを書けばOKです。
category: one のように。

カラム名自体は category_id ですが、references 同様の書き方なのに留意してください。

実際のテストで使用する際は、
@book = books(:one)
のように、テーブル名(YAMLのラベル) を指定します。

中級

YAML の定義で、ID のカラムに即値を書いても構いませんが、
書かなければ(デフォルトそうですが)、データベース登録時に適当な値が割り当てられます。

また、
通常、全てのテストで全ての YAML がデータベースに適用されます。
これは、\test\test_helper.rbfixtures :all と書いているからです。
通常はこれで支障ないですが、
絞り込みたい場合は、
fixtures :all 部分をコメントアウトした上で、テストファイル毎に設定してください。

e.g.
●\test\test_helper.rb

  module ActiveSupport
    class TestCase
      #fixtures :all

●\test\controllers\books_controller_test.rb

  class BooksControllerTest < ActionDispatch::IntegrationTest
    fixtures :books, :users

こうする場合、全てのテストファイルで fixtures を設定しないといけません。
fixtures :all を活かしたまま各ファイルに fixtures を書いても、
各ファイルの設定で上書きされるわけでは無いようです。

テストの種類

テストは、以下に分類されます。

後、Pundit を使う場合は、policies も追加されます。

中級

大昔(Rails 5.0 まで?) のコントローラテストは、
class BooksControllerTest < ActionController::TestCase
のように、機能テストを継承していました。
が、現在は、
BooksControllerTest < ActionDispatch::IntegrationTest
のように、統合テストを継承しています。
つまり、controllers も integration も仕様的には全く同じですが、
用途によって人間が使い分けるということです。
参考: https://api.rubyonrails.org/classes/ActionController/TestCase.html

parallelize

デフォルトでは、テストを複数のスレッドで動作させるように
\test\test_helper.rb で、
parallelize(workers: :number_of_processors, with: :threads)
と書かれています。

が、エラーになる事が多いので、
parallelize(workers: 1)
にしておくのが安全でしょう。

デフォルトでは、
RuntimeError: Could not find a valid mapping for #<User id: ・・・
ActiveRecord::ActiveRecordError: Cannot expire connection, it is owned by a different thread: #<Thread: ・・・
みたいなエラーになる事があります。
複数スレッド間での衝突が起こっていると思われます。

 

setup、teardown

1つのテストファイル中に複数のテストを実装すると思います。
ファイル内共通で使える以下のメソッドが便利です。

 

e.g.
class BooksControllerTest < ActionDispatch::IntegrationTest
  setup do
    @book = books(:one)
    @user = users(:admin)
    sign_in @user
  end

  teardown do
    # 必要なら書く。
  end

 

rails test

ruby bin/rails test
で、System Test 以外 のテストが実行されます。

エラーなど無ければ、
90 runs, 133 assertions, 0 failures, 0 errors, 0 skips
のように表示されて終了します。
エラーが発生したら、その時点でテストを中断し、
エラー内容を表示するので、デバッグします。

エラーメッセージの例:

Failure:
BooksControllerTest#test_should_get_index [test/controllers/books_controller_test.rb:13]:
Expected response to be a <422: unprocessable_entity>, but was a <200: OK>

解説:テストコードでは response が unprocessable_entity になると書いているが、実際には OK だった。

 

部分テスト

指定範囲だけをテストする事も可能です。


後へ      Topへ      次へ