I've been trying to solve a problem for a few weeks now. I am running rspec tests for my Rails app, and they are working fine except for one error that I can't seem get my head around.
I am using MySQL with the InnoDB engine.
I have set config.use_transactional_fixtures = true in spec_helper.rb
I load my test fixtures manually with the command rake spec:db:fixtures:load.
The rspec test is being written for a BackgrounDRb worker, and it is testing that a record can have its state updated (through the state_machine gem).
Here is my problem:
I have a model called Listings. The rspec test calls the update_sold_items method within a file called listing_worker.rb.
This method calls listing.sell for a particular record, which sets the listing record's 'state' column to 'sold'.
So far, this is all working fine, but when the update_sold_items method finishes, my rspec test fails here:
listing = Listing.find_by_listing_id(listing_id)
listing.state.should == "sold"
expected: "sold",
got: "current" (using ==)
I've been trying to track down why the state change is not persisting, but am pretty much lost. Here is the result of some debugging code that I placed in the update_sold_items method during the test:
pp listing.state # => "current"
listing.sell!
listing.save!
pp listing.state # => "sold"
listing.reload
pp listing.state # => "current"
I cannot understand why it saves perfectly fine, but then reverts back to the original record whenever I call reload, or Listing.find etc.
Thanks for reading this, and please ask any questions if I haven't given enough information.
Thanks for your help,
Nathan B
P.S. I don't have a problem creating new records for other classes, and testing those records. It only seems to be a problem when I am updating records that already exist in the database.
I suspect, like nathan, transaction issues. Try putting a Listing.connection.execute("COMMIT") right before your first save call to break the transaction and see what changes. That will break you out of the transaction so any additional rollback calls will be non-effectual.
Additionally, by running a "COMMIT" command, you could pause the test with a debugger and inspect the database from another client to see what's going on.
The other hypothesis, if the transaction experimentation doesn't yield any results, is that perhaps your model really isn't saving to the database. Check your query logs. (Specifically find the update query).
These kind of issues really stink! Good luck!
If you want to investigate what you have in DB while running tests you might find this helpful...
I have a rspec test where I save #user.save and it works like a charm, but then I wanted to see if it's really saved in the DB.
I opened rails console for test environment
rails c test
ran
User.all
and as expected got nothing
I ran my spec that contains:
user_attr_hash = FactoryGirl.attributes_for(:user)
#user = User.new user_attr_hash
#user.save
binding.pry
I thought that stopping the test after save would mean that it's persisted, but that's not the case. It seems that COMMIT on the connection is fired later (I have no idea when:\ )
So, as #Tim Harper suggests, you have to fire that commit yourself in the pry console:
pry(#<RSpec::Core::ExampleGroup::Nested_1>)> User.connection.execute("COMMIT")
Now, if you run User.all in your rails console you should see it ;)
Related
This issue only exists in test environment. Everything runs fine in development environment.
I am facing a strange issue after recently upgrading to Rails 5.0.0.1 from Rails 4.2.7.1. Everything was working fine before this upgrade.
In one of my models, I use ActiveJob to perform a task.
# webhook_invocation.rb
def schedule_invocation
WebhookRequestJob.perform_later(id)
end
def init
remember_webhook # No DB changes
init_errors_context # No DB changes
flow_step_invocation.implementation = self
flow_step_invocation.save!
return unless calculate_expressions # No DB changes
calculated! # An AASM event, with no callbacks
schedule_invocation
end
and in WebhookRequestJob#perform, I retrieve the object using the ID supplied
# webhook_request_job.rb
def perform(webhook_invocation_id)
invocation = WebhookInvocation.find_by(id: webhook_invocation_id)
invocation.run_request
end
The problem is that in the #perform, it cannot find the record (invocation becomes nil). I even tried putting p WebhookInvocation.all as the first line, but all it prints is an empty collection. On the other hand, if I try p WebhookInvocation.all in #schedule_invocation method, it properly prints out all the objects of WebhookInvocation.
There is no exception being raised, no lines of warnings either.
Edit 1:
I even tried passing the object directly to #perform_later i.e. WebhookRequestJob.perform_later(self), but the received object at #perform is nil.
Edit 2:
I noticed that there are some messages like Creating scope :fail. Overwriting existing method FlowStepInvocation.fail, caused by using AASM. I eliminated them by using create_scopes: false. But that still didn't solve the problem.
My guess from the info you supplied is that you have are calling the schedule_invocation method in a after_save or after_create callback. Since the callback is called, ActiveJob might start processing the job even before the object is actually persisted (before COMMIT is done). In this case your record will not show up in the database when job is processed and you will get an empty collection or nil.
To fix this change your callback to after_commit to make sure that the COMMIT action has happened before you queue the job.
It turns out that config.active_job.queue_adapter was set to :inline as default before Rails 5, but it is set to :async in Rails 5.
This made the specs to fail (don't know why). To resolve this, I put the following line in my config/environments/test.rb:
config.active_job.queue_adapter = :inline
I'm having an issue with my specs. I'm trying to run a spec that creates and destroys an associated object, but none of my specs are creating or destroying that object. The weird thing is, I can literally copy and paste every line of code (except for the .should) out of each test into the console and the console will run each expectation perfectly, creating and destroying those objects. Here's a sample:
it "should not destroy notification for like on comment" do
comment = FactoryGirl.create(:comment)
like = FactoryGirl.create_list(:like, 2, likable: comment)
like.first.destroy
note = comment.user.notifications.find_by(notifiable: comment, from_comment: false)
note.should_not be_nil
end
Only the first like on a specific comment will send a notification to that comment's author. And if a comment only has one like, and that like is destroyed, it will destroy the notification it had originally sent, but it won't destroy the notification if the comment has more than 0 likes at any time.
Is the problem that I'm just not writing scalable code? I know my specs probably aren't perfect, but why is the console getting different results with identical input?
Are you running your rails console in test environment or in development as it defaults to?
Different database and environment configurations might be the reason for different behavior, also the note object might already exist in development db at the time you test it manually in console.
Either try running console in test environment:
$ rails c test
or better temporarily put debugger line inside your test code (you will need get 'debugger' gem installed if don't have it already):
it "should not destroy notification for like on comment" do
comment = FactoryGirl.create(:comment)
like = FactoryGirl.create_list(:like, 2, likable: comment)
debugger
like.first.destroy
note = comment.user.notifications.find_by(notifiable: comment, from_comment: false)
note.should_not be_nil
end
I recently upgraded an app i was working on to rails 3.1rc5..
For the most part it's been great but a few of my tests are having really weird issues..
For example in one of my cucumber specs i create a bunch of fake records using factory girl.. usually this works fine but it seemed that it wasn't creating the records for some reason..
So I commented out all of my factory stuff and replaced it with this:
c = Contact.new(:first_name => "SOMEONE", :last_name => "COOL", :dob => 10.years.ago, :sex => "male")
if c.save
puts "MYCOUNT: #{Contact.count}"
else
puts "EXPLOSIONS!!!"
end
Running this as part of the cucumber suite outputs this:
MYCOUNT: 0
So the contact record is obviously being saved (and passing validations) yet it is still not showing up when i call count??
Why??
I am using:
Rails 3.1rc5
rspec-rails
cucumber-rails
and
factory_girl_rails
I should also probably note i'm indexing my models using sunspot (solr API) https://github.com/outoftime/sunspot
It sounds like you've got a transaction rollback firing within your test:
1) Transaction opened
2) Contact.save succeeds (now have legit Contact instance and db record)
3) Something goes wrong, raises ActiveRecord::Rollback
4) Transactions rolls back, leaving legit Contact instance but no db record, count = 0
I don't know what would cause this related to your Rails upgrade, but perhaps this will help you find what's failing.
EDIT:
If you tail your log/test.log file you should see a Rollback occur if this is the case. You can look at the previous DB activity to get a clue as to what the last successful db operation was before it.
Not an exact science, but may help you decide if this is the case and get a rough idea of where it went sideways.
I'd like to init the data base once everytime i run tests, rather than every test.
I know with Rspec there is before(:all), but I haven't been able to get that working. I was wondering if rails had something similar.
Firstly: there used to be a before(:all) equivalent in Test::Unit but it was removed (don't know why).
Secondly: there are very good reasons not to do what you are trying to do - tests are meant to be run independently of one another, not rely on state that's in the db. This way you can guarantee that it's testing exactly what you are expecting it to test.
If you have one test that changes the state of the db, and you move it and it runs after another test which expects it to be another state - you run into problems. Thus, all test must be independent.
Thus: the db is rolled back to its pristine state and re-seeded every time.
If you really want some state that the db is always in - then set it up in the fixtures... and just realise that the db will be re-loaded for each test.
If you are having trouble with load-times... then consider figuring out some other way around the problem - eg don't use huge numbers of fixtures, instead use Factories to only create the data that you need for each individual test.
If there's some other reason... let us know - we may have a solution for it.
Edit: if you really need it, I actually wrote a monkey patch for this a long while back:
"faking startup and shutdown"
All things to run before everything just go in the top of the class
require 'test_helper'
class ObjectTest < ActiveSupport::TestCase
call_rake("db:bootstrap RAILS_ENV=test")
#set up our user for doing all our tests (this person is very busy)
#user = Factory(:user)
#account = Factory(:account)
#user.account = #account
#user.save
# make sure our user and account got created
puts "||||||||||||||||||||||||||||||||||||||||||||||"
puts "| propsal_test.rb"
puts "| #{#user.name}"
puts "| #{#user.account.name}"
puts "||||||||||||||||||||||||||||||||||||||||||||||"
I'm trying to perform an integration test via Watir and RSpec. So, I created a test file within /integration and wrote a test, which adds a test user into a base via factory_girl.
The problem is — I can't actually perform a login with my test user. The test I wrote looks as following:
...
before(:each)
#user = Factory(:user)
#browser = FireWatir::Firefox.new
end
it "should login"
#browser.text_field(:id, "username").set(#user.username)
#browser.text_field(:id, "password").set(#user.password)
#browser.button(:id, "get_in").click
end
...
As I'm starting the test and see a "performance" in browser, it always fires up a Username is not valid error.
I've started an investigation, and did a small trick. First of all I've started to have doubts if the factory actually creates the user in DB. So after the immediate call to factory I've put some puts User.find stuff only to discover that the user is actually in DB. Ok, but as user still couldn't have logged in I've decided to see if he's present in DB with my own eyes.
I've added a sleep right after a factory call, and went to see what's in the DB at the moment. I was crushed to see that the user is actually missing there! How come? Still, when I'm trying to output a user within the code, he is actually being fetched from somewhere. So where does the records, made by factory_girl within a runtime lie? Is it test or dev DB? I don't get it.
I've 10 times checked if I'm running my Mongrel in test mode (does it matter? I think it does, as I'm trying to tun an integration test) and if my database.yml holds the correct connection specific data.
I'm using an authlogic, if that can give any clue (no, putting activate_authlogic doesn't work here).
Don't forget that RSpec is probably using transations when running the specs. RSpec will wrap the execution of the spec within a transaction and rollback at the end. It means you won't be able to see the record from outside that transaction (i.e. from another SQL connection).
If you want to ensure the user record is actually created by Factory Girl, you can do something like:
before(:each)
#user = Factory(:user)
User.find_by_username(#user.username).should_not be_nil
#browser = FireWatir::Firefox.new
end
Somehow the solution went strange — I put factories to before(:all) block, and all the stuff worked as it should.
Factory Girl is going to create temporary DB entries in your test database. Your tests database is going to be cleared out after each test.