はじめに
READYFOR でエンジニアとして働いている masaki です。
この記事は READYFOR Advent Calendar 2023 の14日目の記事になります。(投稿遅れました…🙇)
他の記事もぜひご覧ください!
Rails テスティングガイド - Railsガイド を読んでいると以下のような記載があります。
- フィクスチャに対応するテーブルに含まれている既存のデータをすべて削除する
- フィクスチャのデータをテーブルに読み込む
- フィクスチャに直接アクセスしたい場合はフィクスチャのデータをメソッドにダンプする
説明を読んで、そうなのか〜と思いつつ、どんなコードでそうなっているのか(なぜか)興味が湧き、少し調べたことがあったのでその内容を共有させていただこうと思います。
大まかな処理の流れを追うだけで、細かい実装されている内容については飛ばしています。おかしい点があれば指摘していただけるとありがたいです。
※ちなみ READYFOR では RSpec と factory_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.rb
で fixtures
というメソッドが呼び出されています。
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つのことをやっています。
- 引数をもとに、テストで読み込むフィクスチャ名を
class_attribute :fixture_table_names
に設定する - フィクスチャのデータにアクセスするためのプライベートメソッドを作成する
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 のコードを読むと今まで見たことがないコードを学ぶ機会となって、とても勉強になるなと思いました。
読んでくださった方、ありがとうございました🙏