2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

yothioAdvent Calendar 2023

Day 13

【Rails】テスト実行後にDBをキレイにするのにGemを使う択もアリ

Posted at

概要

プログラムを作成すると多くの場合はテストコードを作成すると思います
Railsでテストを実行しているときに「Userデータ作成、作成したらXXも合わせて作成される」みたいなケースがあったときに開発者はあまり考えることはせずともテスト実行後に作成したデータが消えていることが多いと思います

Railsには標準でその仕組が存在し見ていきます
また標準ではなくDataBaseCleanerDatabaseRewinderといったGemを利用する選択もあるので、Gemを利用するケースについて考えてみます

名前 バージョン
Rails 7.2.0-alpha
rspec-rails 6.2.0.pre

※ バージョンは2023/12/13の各Gemのmainブランチ

テストデータの自動ロールバックについて

結論: テスト実行時にトランザクションを開始し、テストケースが終了する毎にトランザクションへロールバックしてデータを戻します

詳細な実装は ActiveRecord::TestFixturesにあります

Railsガイドに書いてあるとおり、TestFixtures内にuse_transactional_testsを利用したロジックが存在し、それによってテスト実行前(before_setupメソッド内)にトランザクションを開始し、テスト実行後(after_teardownメソッド内)でトランザクションのロールバックを行いデータを巻き戻してます

before_setupafter_teardownがテスト前後のコールバックで呼び出される実態はActiveSupport::Testing::SetupAndTeardownに存在します

基本的にはGemを導入したり、自前でデータ削除を行わなくてもこの仕組みによってテストデータをクリーンに保てます

Gemを利用するケース

そこを考えるとGemを利用するケースとしてはトランザクションでデータを作成・削除が行えないケースになると思います
たとえばRailsガイドでは「並列トランザクションをテストする」という項目でテストケースのクラスでトランザクションを無効にする例を提示しています

並列トランザクションをスレッドで実行するコードをテストしたい場合、テスト用トランザクションの下にすでにネストされているため、トランザクションが互いにブロックしてしまう可能性があります。
self.use_transactional_tests = falseを設定すると、テストケースのクラスでトランザクションを無効にできます。

またテスト実行中(テスト対象のコード)でコネクションを接続する処理を記述した場合はトランザクションが新しく開始され、コネクション接続後に接続前に作成したデータの参照がうまくできなくなります

spec/models/content_spec.rb
  describe do
    before do
      Content.create(title: 'hoge', name: 'fuga')
      Content.establish_connection(:default)
      Content.create(title: 'hoge', name: 'fuga')
    end
    it do
      expect(Content.count).to eq 2 # error Content.countは1で返ってくる
    end
  end

これもRailsの標準のデータを戻す手段であるトランザクションを利用しているために発生するので、トランザクションを無効にし、Gemを利用してデータを削除するとよいでしょう

次にRailsのテストフレームワークとしては use_transactional_tests のパラメーターをfalseにすることでトランザクションでクリーンにするのをやめていますが、RSpecの場合を見てみます

RSpecだとどうやってトランザクションを管理をやめるか

RSpecの場合はuse_transactional_fixturesという名前で同様の設定ができます

具体的にはspec/rails_helper.rbもしくはspec/spec_helper.rb内のRspec.configuration.use_transactional_fixturesです

spec/rails_helper.rb
Rspec.configuration do |config|
  config.use_transactional_fixtures = true # or false
end

use_transactional_fixturesに設定した値がActiveRecord::TestFixtures.use_transactional_testsを上書きしてるのでそのようなことができます
この記述はRSpec::Rails::FixtureSupportに存在します

lib/rspec/rails/fixture_support.rb
module RSpec
  module Rails
    module FixtureSupport
      if defined?(ActiveRecord::TestFixtures)
        extend ActiveSupport::Concern
        ....
        include ActiveRecord::TestFixtures
        ....
        included do
            ....
            self.use_transactional_tests = RSpec.configuration.use_transactional_fixtures
            self.use_instantiated_fixtures = RSpec.configuration.use_instantiated_fixtures

終わりに

最近接続の切り替えを手動で行う事を多く調べ、その際にテストで詰まっていたので調べた内容をまとめました

コネクション接続を明示的に指定し、テストがうまく通らなくなったなどのケースが発生したときに参考になれば幸いです

2
1
0

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
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?