Reputation: 1832
I have a paper model. Which has two states: draft and approved.
I have a form on edit_paper_path which makes a put with remote: true.
My controller:
def update
paper = Paper.find(params[:id])
puts paper.status # => :draft
paper.approved!
puts paper.status # => :approved
end
My test is:
it 'changes status to Approved', js: true do
expect {
click_button 'Approve'
}.to change { paper.status }
end
But the test fails, and I noticed that the change done in the controller on the model, is lost, so the status keeps being :draft.
Additional:
This is my database_cleaner config:
RSpec.configure do |config|
config.before(:suite) do
DatabaseCleaner.clean_with(:truncation)
end
config.before(:each) do
DatabaseCleaner.strategy = :transaction
end
config.before(:each, js: true) do
DatabaseCleaner.strategy = :truncation
end
config.before(:each) do
DatabaseCleaner.start
end
config.append_after(:each) do
DatabaseCleaner.clean
end
end
Any ideas?
Upvotes: 2
Views: 659
Reputation: 102443
Your test is failing since the controller updates the record in the database while your test is operating on the model held in memory.
What you need to do is sync the representation in memory with the DB:
it 'changes status to Approved' do
expect(paper.approved?).to be_falsy # sanity check
click_button 'Approve'
# trick to get capybara to wait for request to finish
# note that there actually needs to be a flash message
expect(page).to have_css(".flash")
paper.reload
expect(paper.approved?).to be_truthy
end
Edited with feedback from Anthony E to deal with race conditions.
Upvotes: 2
Reputation: 11245
Capybara AJAX requests run asynchronously, so paper.status
is being evaluated before the value is updated in the database.
In fact, Capybara is decoupled from the database, so you should try to test your ajax update by querying the DOM itself using the typical has_content
/has_css
query methods Capybara provides.
You could fix this by using sleep
to wait for the transaction to finish, but that's a bit of a hacky solution and isn't guaranteed to pass depending on how long it takes for the database commit to take place.
Another option is wait until the AJAX call completes by using a jQuery.active
query script. There's a good article that explains this approach here: https://robots.thoughtbot.com/automatically-wait-for-ajax-with-capybara.
Upvotes: 2