LoginSignup
5
1

[Ruby on Rails] テストのフィクスチャの動作に関するコードを読んでみた

Last updated at Posted at 2023-12-15

READYFORアドベントカレンダー2023カバー画像.png

はじめに

READYFOR でエンジニアとして働いている masaki です。
この記事は READYFOR Advent Calendar 2023 の14日目の記事になります。(投稿遅れました…🙇)

他の記事もぜひご覧ください!

Rails テスティングガイド - Railsガイド を読んでいると以下のような記載があります。

  1. フィクスチャに対応するテーブルに含まれている既存のデータをすべて削除する
  2. フィクスチャのデータをテーブルに読み込む
  3. フィクスチャに直接アクセスしたい場合はフィクスチャのデータをメソッドにダンプする

説明を読んで、そうなのか〜と思いつつ、どんなコードでそうなっているのか(なぜか)興味が湧き、少し調べたことがあったのでその内容を共有させていただこうと思います。
大まかな処理の流れを追うだけで、細かい実装されている内容については飛ばしています。おかしい点があれば指摘していただけるとありがたいです。

※ちなみ READYFOR では RSpecfactory_bot を使って Rails のテストを書いているので、個人の興味本位で調べた内容になります

対象のバージョン等

調査した Rails のバージョンは Rails 7.0.8 で、以下のコマンドを実行して作ったアプリ使っています。 (Rails 7.1 ではないので、最新とは異なるかもしれないです...🙏)

> rails new blog
> rails generate scaffold Article title:string content:text

fixtures の読み込み処理を探す

とりあえず関係ありそうな処理がないかエディタ(VSCode)の検索機能を使って、探してみます。fixture で検索をかけてみると test_helper.rbfixtures というメソッドが呼び出されています。

class ActiveSupport::TestCase
  # 略
  # Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order.
  fixtures :all
  # 略
end

コメントからもここでフィクスチャのセットアップが行われていることがわかります。メソッドの定義元は ActiveRecord::TestFixtures だったので、こちらのコードを見ていきます。

ActiveRecord::TestFixtures を読む

対象のコードは以下のものです。

rails/activerecord/lib/active_record/test_fixtures.rb at v7.0.8 · rails/rails

#fixtures メソッド

処理の内容としては次の2つのことをやっています。

  1. 引数をもとに、テストで読み込むフィクスチャ名を class_attribute :fixture_table_names に設定する
  2. フィクスチャのデータにアクセスするためのプライベートメソッドを作成する

test_helper.rb のコメントにあったように、引数が :all の場合は test/fixtures/*.yml に該当する全てのファイル名が fixture_table_names が設定されます。
その後、#setup_fixture_accessors メソッドが呼び出され、読み込んだフィクスチャに直接アクセスするためのプライベートメソッドが作成されています。モジュールと #define_method を活用して、動的にメソッドを include しているようです。

各テストクラスは fixtures メソッドを呼び出している ActiveSupport::TestCase を継承しているため、設定が全テストで適応されます。

# デバックでの確認
❯ rails test
# (省略)
     3| require 'test_helper'
     4|
     5| class ArticlesControllerTest < ActionDispatch::IntegrationTest
     6|   setup do
=>   7|     binding.break
     8|     @article = articles(:one)
     9|   end
# (省略)
(rdbg) ArticlesControllerTest.ancestors.include? ActiveSupport::TestCase
true
(ruby) fixture_table_names
["articles"]
(ruby) ArticlesControllerTest.private_instance_methods.grep /articles/
[:articles]

これで読み込み対象のフィクスチャの設定と、フィクスチャのデータにアクセスするためのメソッドの作成の流れは分かりました。後は、テスト実行前のデータの読み込み処理を見てみます。

#setup_fixtures メソッド

各テストの実行前(#setup 前)の #before_setup で、#setup_fixtures が実行されるようになっており、ここでフィクスチャのデータの読み込み処理が行われています。テーブルへのデータの投入処理は、#load_fixtures メソッドを呼び出して実行しており、ここで fixture_table_names が参照されています。このデータの投入処理は初回のみ実行され、それ以降は初回に読み込んだフィクスチャのデータを利用しています。(テストの設定にもよると思います。)

#teardown_fixtures メソッド

各テストの実行後(#teardown 後)は、#after_teardown 内の #teardown_fixtures によって、トランザクションをロールバックしてテストでのデータの変更をリセットしています。

トランザクションについては以下の記事などが参考になるかもしれないです。

最後に

フィクスチャの動作の流れを大まかに見ていったので、まだまだ理解できていない箇所はたくさんありますが、gem のコードを読むと今まで見たことがないコードを学ぶ機会となって、とても勉強になるなと思いました。

読んでくださった方、ありがとうございました🙏

5
1
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
1