initial-test-data
The initial-test-data
is a tool to create a text fixture for Rails applications.
Overview
Although the Rails itself has a standard mechanism to initialize the database in the test environment (ActiveRecord::FixtureSet), it is rather cumbersome to construct a real (often complicated) data structure from YAML files.
The initial-test-data
provides a way to create a test fixture using Active Record, Factory Girl, etc.
It also offers utility methods called store
and fetch
to register and access the initialized data.
Installation
Add the following line to Gemfile
:
gem 'initial-test-data', group: :test
Run bin/bundle install
on the terminal.
Configuration of test framework
MiniTest
Edit test/test_helper.rb
like this:
ENV['RAILS_ENV'] ||= 'test'
require File.expand_path('../../config/environment', __FILE__)
require 'rails/test_help'
require 'initial-test-data/factory_girl' # If you use the Factory Girl
InitialTestData.import
class ActiveSupport::TestCase
include InitialTestData::Utilities
# (snip)
end
Note that the default value of the first argument of import
method is 'test'
,
so you can omit it when your test scripts are located in the test
directory.
RSpec
Edit spec/rails_helper.rb
like this:
ENV["RAILS_ENV"] ||= 'test'
require 'spec_helper'
require File.expand_path("../../config/environment", __FILE__)
require 'rspec/rails'
require 'initial-test-data/factory_girl' # If you use the Factory Girl
RSpec.configure do |config|
config.include InitialTestData::Utilities
config.before(:suite) do
InitialTestData.import('spec')
end
end
Usage
Synopsis
- Create
initial_data
subdirectory in thetest
orspec
directory. - Put any ruby scripts on this file.
- Run your test suite.
Caching data
The initial-test-data
generates the md5 digest of all ruby scripts in
the initial_data
directory and app/models
directory,
then stores it to the _initial_data_digest
table of the test database.
When the generated md5 digest is equal to the previously stored value, data initializing process will be skipped.
Load Order
The files in the initial_data
directory are loaded in alphabetical order.
If you control the load order, you should make a YAML file called _index.yml
,
which has a content like this:
- products
- customers
- orders
Options for the InitialTestData.import
method
except
The initial-test-data
utilizes the database_cleaner
gem to truncate
all tables except _initial_data_digest
and schema_migrations
.
If you want to keep some tables intact, specify the except
option:
RSpec.configure do |config|
config.before(:suite) do
InitialTestData.import('spec', except: %w(country_names))
end
end
only
Contrary to the except
option, when you use this,
only the specified tables are initialized:
RSpec.configure do |config|
config.before(:suite) do
InitialTestData.import('spec', only: %w(customers products))
end
end
Note that the except
and only
options are used to construct
the options passed to the DatabaseCleaner.strategy=
method.
monitoring
By default the initial-test-data
monitors the initial_data
directory
under the test
directory (or the directory specified by the first argument)
and the app/models
directory.
If you want to add monitoring target directories, specify monitoring
option to the InitialTestData.import
method:
RSpec.configure do |config|
config.before(:suite) do
InitialTestData.import('spec',
monitoring: [ 'app/services', 'lib', 'spec/factories' ]
end
end
You should use relative paths from the Rails.root
.
quiet
Specify true
to this option in order to suppress the info messages from the
initial-test-data
.
Utility methods: store
and fetch
The initial-test-data
provides two utility methods, store
and fetch
,
to make it easy to refer the initialized records in your tests.
You can use the store
method within the initialization scripts
in order to register an ActiveRecord object by name:
include InitialTestData::Utilities
# Using Active Record
store(Customer.create(...), :john)
store(ShopOwner.create(...), :mike)
# Using Factory Girl
store(create(:customer, ...), :john)
store(create(:shop_owner, ...), :john)
Then, you can get this record with fetch
method in the test scripts:
customer = fetch(:customer, :john)
shop_owner = fetch(:shop_owner, :mike)
The fetch method treats :john
and "john"
as the same key.
Note that the initial-test-data
creates a YAML file
named initial_data_record_ids.yml
in the tmp
directory
to track the primary key values of registered records.
Please do not remove or tamper it.
Environment variable REINIT
If you want to enforce the initialization process, add REINIT=1
before the command:
REINIT=1 bin/rake test
When you want to skip it, place REINIT=0
before the command:
REINIT=0 bin/rake test
Example
MiniTest and Active Record
# test/initial_data/customers.rb
include InitialTestData::Utilities
0.upto(9) do |n|
c = Customer.create(
email: "test#{n}@example.com",
given_name: 'John',
family_name: 'Doe'
)
store(c, "test#{n}")
end
# test/integration/manage_customers_test.rb
require 'test_helper'
class ManageCustomersTest < ActionDispatch::IntegrationTest
test "Change the name of a customer" do
customer = fetch(:customer, :test0)
get "/customers/#{customer.id}/edit"
assert_response :success
patch "/customers/#{customer.id}",
customer: { given_name: 'Mike', family_name: 'Smith' }
assert_redirected_to [ assigns(:customer) ]
follow_redirect!
assert_select "h1", "Listing Customers"
customer.reload
assert_equal 'Mike', customer.given_name
assert_equal 'Smith', customer.family_name
end
end
RSpec, Capybara and Factory Girl
# Gemfile
...
group :test do
gem 'rspec-rails'
gem 'factory_girl_rails'
gem 'initial-test-data'
end
...
# spec/rails_helper.rb
ENV["RAILS_ENV"] ||= 'test'
require 'spec_helper'
require File.expand_path("../../config/environment", __FILE__)
require 'rspec/rails'
require 'initial-test-data/factory_girl'
FactoryGirl.reload
...
# spec/factories/customers.rb
FactoryGirl.define do
factory(:customer) do
sequence(:email) { |n| "test#{n}@example.com" }
given_name 'John',
family_name 'Doe'
end
end
# spec/initial_data/customers.rb
include FactoryGirl::Syntax::Methods
include InitialTestData::Utilities
0.upto(9) do |n|
c = create(:customer, email: "test#{n}@example.com")
store(c, "test#{n}")
end
# spec/features/manage_customers_spec.rb
require 'rails_helper'
feature 'Manage customers' do
let(:customer) { fetch(:customer, :test0) }
scenario 'Change the name of a customer' do
visit root_path
find('table.customers td', text: customer.email)
.find(:xpath, '..').click_link('Edit')
fill_in 'Given Name', with: 'John'
fill_in 'Family Name', with: 'Doe'
click_button 'Update'
customer.reload
expect(customer.given_name).to eq('John')
expect(customer.family_name).to eq('Doe')
end
end
License
The initial-test-data
is distributed under the MIT license. (MIT-LICENSE)
Author
Tsutomu Kuroda (t-kuroda@oiax.jp)