これは Ruby not on Rails の記事です。
non-RailsでActive Recordを使う方法は検索するといくつか出てきますが、fixtureについては出てこないみたいなので書きます。
Active Recordを単体で使う方法は参考文献に挙げたのでそちらを参照してください。ディレクトリ構成やファイル配置などはいろいろやり方があると思いますので適宜読み替えてください。
fixtureはテストで使うのでテストフレームワークが問題になりますが、わたしはRSpecを使っています。とはいえ必要なことはどのフレームワークでもあまり変わらないはずです。
Active Recordのバージョンは6.0.2.1でやっています。
テスト
- テストのクラスで
ActiveRecord::TestFixturesをincludeする。- minitestなら
MiniTest::Testのサブクラス、RSpecならexample groupのコンテキスト(describeやcontextの中でitの外)でincludeするということです。
- minitestなら
- テストのクラスで
self.fixture_path=を設定する。-
use_transactional_testsやuse_instantiated_fixturesなどの設定が必要な場合は同様に設定できます。
-
- テストのクラスで
fixtures :usersのようにして使うfixtureを指定する。-
fixtures :allですべてのfixtureを指定したのと同じになります。
-
- (minitest以外では)lifecycle hookでテストの実行前に
setup_fixturesが、実行後にteardown_fixturesが実行されるようにする。- minitestでは
ActiveRecord::TestFixturesが面倒を見てくれる(before_setupとafter_teardownに追加してくれる)ので大丈夫。 - RSpecではそれぞれ
beforeとafterで呼ぶ。(before(:context)でfixtureを使うには工夫が必要そうです)
- minitestでは
- (Rails < 6.1)
ActiveSupport::TestCaseで定義されているmethod_nameメソッドが呼ばれるので用意しておく必要がある。- これはminitestで定義されている
nameのaliasなので、minitestを使っているなら同じようにaliasとして定義すればよい。 - Rails >= 6.1では
nameを使うように修正済み -
ActiveSupport::TestCaseはMiniTest::Testの薄いラッパなのでこちらを使ってしまうのもありかもしれません。どうせActive RecordはActive Supportに依存しているわけですし。
- これはminitestで定義されている
- minitest以外では、minitestで定義されている
name(Rails >= 6.1) あるいはActiveSupport::TestCaseで定義されているmethod_name(Rails < 6.1) が呼ばれるので何かしら定義しておく必要がある。- これはminitestでは
test_exampleのようなテストメソッドのメソッド名を返すので、これに準じてテスト固有の名前を取れるならそれを返すのが望ましい。 - RSpecでは
RSpec.current_example.descriptionで取れる。
- これはminitestでは
- RSpecを使っている場合は
rspec-railsのRSpec::Rails::FixtureSupportをincludeするとこのあたりの面倒を見てくれそうな気がしますが、Railsがいない場合はそのままでは動かなさそうです。
まとめると次のような感じになります。
require 'minitest/autorun'
require 'active_record'
require './app/models/user'
class UserTest < MiniTest::Test
include AcitveRecord::TestFixtures
self.fixture_path = './test/fixtures'
fixtures :users
alias method_name name
def test_example
assert_equal 'Yukihiro Matsumoto', users(:matz).name
end
end
require 'active_record'
require './app/models/user'
RSpec.describe User do
include ActiveRecord::TestFixtures
self.fixture_path = './spec/fixtures'
fixtures :users
# Rails >= 6.1
def name
RSpec.current_example.description
end
# Rails < 6.1
def method_name
RSpec.current_example.description
end
before { setup_fixtures }
after { teardown_fixtures }
it { expect(users(:matz).name).to eq 'Yukihiro Matsumoto' }
end
必要な記述をひとまとめにしたヘルパーmoduleを作ったりしてもよいでしょう。お好みでどうぞ。
Rake
Rakeタスクdb:fixtures:loadも使えます。
Rakefileでいい感じに設定をしてActive RecordのRakeタスクたちを読み込んでやります。
require 'active_record'
ActiveRecord::Tasks::DatabaseTasks.env = ENV['APP_ENV'] || 'default'
ActiveRecord::Tasks::DatabaseTasks.database_configuration = YAML.load_file('./config/database.yml')
ActiveRecord::Tasks::DatabaseTasks.db_dir = './db'
ActiveRecord::Tasks::DatabaseTasks.fixtures_path = './spec/fixtures'
ActiveRecord::Tasks::DatabaseTasks.root = Dir.pwd
task :environment do
Dir.glob('./app/models/*.rb') {|file| require file }
ActiveRecord::Base.configurations = ActiveRecord::Tasks::DatabaseTasks.database_configuration
ActiveRecord::Base.establish_connection(ActiveRecord::Tasks::DatabaseTasks.env.to_sym)
end
load 'active_record/railties/databases.rake'
db:fixtures:loadを使うにはActiveRecord::Tasks::DatabaseTasks.fixtures_path=が肝心です。他は適宜設定してください。
また、モデルをrequireしておく必要があります。モデルが読み込まれていなくてもタスクを読み込むことはできますが、実行時に下のようにbelongs_toassociationを認識できずに落ちます(fixtureをauthor_idではなくauthorにラベルで指定する形で書いている場合)。
$ rake db:fixtures:load
rake aborted!
ActiveRecord::Fixture::FixtureError: table "books" has no columns named "author".
なお、上ではモデルをenvironmentタスクでrequireしていますが(environmentタスクはActive Recordのタスクが実行される前に呼ばれます)、Rakefileのトップレベルでもいいと思います。