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

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

Railsには標準でその仕組が存在し見ていきます
また標準ではなく[`DataBaseCleaner`](https://github.com/DatabaseCleaner/database_cleaner)や[`DatabaseRewinder`](https://github.com/amatsuda/database_rewinder)といったGemを利用する選択もあるので、Gemを利用するケースについて考えてみます

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

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

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

詳細な実装は [`ActiveRecord::TestFixtures`](https://github.com/rails/rails/blob/main/activerecord/lib/active_record/test_fixtures.rb)にあります



[Railsガイド](https://railsguides.jp/testing.html#%E4%B8%A6%E5%88%97%E3%83%88%E3%83%A9%E3%83%B3%E3%82%B6%E3%82%AF%E3%82%B7%E3%83%A7%E3%83%B3%E3%82%92%E3%83%86%E3%82%B9%E3%83%88%E3%81%99%E3%82%8B)に書いてあるとおり、`TestFixtures`内に`use_transactional_tests`を利用したロジックが存在し、それによってテスト実行前(`before_setup`メソッド内)にトランザクションを開始し、テスト実行後(`after_teardown`メソッド内)でトランザクションのロールバックを行いデータを巻き戻してます

:::note info
`before_setup`と`after_teardown`がテスト前後のコールバックで呼び出される実態は[`ActiveSupport::Testing::SetupAndTeardown`](https://github.com/rails/rails/blob/8278626a2c0e0ee4dc762891a91e70708380b4d6/activesupport/lib/active_support/testing/setup_and_teardown.rb)に存在します
:::


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

### Gemを利用するケース

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

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


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

```ruby: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`です

```ruby: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`に存在します

```ruby: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
```

https://github.com/rspec/rspec-rails/blob/main/lib/rspec/rails/fixture_support.rb

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

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