I am writing tests for my controllers in admin namespace. Using RSpec (3.5.0), FactoryGirl (4.8.0), DatabaseCleaner (1.5.3) and Mongoid (6.0.3).
The problem is that these test act strangely. When testing GET index
request, objects produced by FactoryGirl are successfully created and persisted. However, the controller does not seem to find them.
I have three different controllers. 2 out of 3 have this problem and the third one works like a charm. The code is the same (except for naming) the only difference is that the resource for the working controller is nested.
The one for accessories works:
describe "GET #index", get: true do
let (:accessory) { FactoryGirl.create(:accessory) }
before do
get :index, params: { category_id: accessory.category_id.to_s }, session: valid_session, format: :json
end
it "responses with OK status" do
expect(response).to have_http_status(:success)
end
it "responses with a non-empty Array" do
expect(json_body).to be_kind_of(Array)
expect(json_body.length).to eq(1)
end
it "responses with JSON containing accessory" do
expect(response.body).to be_json
expect(json_body.first.with_indifferent_access).to match({
id: accessory.to_param,
name: 'Test accessory',
description: 'This is an accessory',
car_model: 'xv',
model_year: '2013',
images: be_kind_of(Array),
category_id: accessory.category.to_param,
dealer_id: accessory.dealer.to_param,
url: be_kind_of(String)
})
end
end
And the one for categories does not:
describe "GET #index", get: true do
let (:category) { FactoryGirl.create(:category) }
before do
get :index, params: {}, session: valid_session, format: :json
end
it "responses with OK status" do
expect(response).to have_http_status(:success)
end
it "responses with a non-empty Array" do
expect(json_body).to be_kind_of(Array)
expect(json_body.length).to eq(1)
end
it "responses with JSON containing category" do
expect(response.body).to be_json
expect(json_body.first.with_indifferent_access).to match({
id: category.to_param,
name: 'Test category',
image: be_kind_of(String),
url: be_kind_of(String)
})
end
end
As you can see the logic is the same: issuing a request in the before
hook and using let
to set the object.
Another strange thing is that GET show
test for categories with the same logic works perfectly.
In these questions (1, 2) they say it might be due to DatabaseCleaner strategy and one should use truncation
instead of transaction
strategy. Which I do since Mongoid allows only truncation
. And I am also not using JavaScript-enabled tests and specifically told rspec to use_transactional_fixtures = false
RSpec config for FactoryGirl and DatabaseCleaner:
RSpec.configure do |config|
config.include FactoryGirl::Syntax::Methods
config.before(:suite) do
DatabaseCleaner.strategy = :truncation
DatabaseCleaner.clean_with(:truncation)
end
config.before(:each, :js => true) do
DatabaseCleaner.strategy = :truncation
end
config.before(:each) do
DatabaseCleaner.start
end
config.after(:each) do
DatabaseCleaner.clean
end
end
I am able to make these tests pass by issuing request and creating an object in each example instead of using before
and let
. But I think it should work with them.
Controller index methods are default:
def index
@thing = Thing.all
end
Do you have any thoughts on this strange behaviour?
Please try let!
instead of let
.
Note that let
is lazy-evaluated. Category data is generated when you call category.to_param
. It does not exist in the before
block.
See also https://relishapp.com/rspec/rspec-core/v/3-5/docs/helper-methods/let-and-let