I'm having a weird problem with my Rspec test suite. All tests that insert data into a table that has a unique constraint fail. Running the failing tests individually by specifying the line number works as expected.
To investigate the issue I printed the number of rows in that table before inserting the data that is causing the constraint exception and it reports that the table is empty.
There is no before :all in any file at all.
I'm using DatabaseCleaner and, except for this issue, it seems that the cleaning is working.
This e.g. doesn't work when running the whole file:
describe User
# ...
describe '#follow_location' do
let(:user) { Factory(:user) }
let(:location) { Factory(:location) }
it 'should raise when trying to follow an already followed location' do
puts LocationFollowship.count # => 0
user.followed_locations << location # exception raised
lambda {
user.follow_location location
}.should raise_error User::AlreadyFollowingLocation
end
end
# …
end
EDIT:
I was able to track it down. It has to do with Rails using a cached statement. Calling ActiveRecord::Base.connection.clear_cache! makes it work. But adding this snippet in spec_helper.rb causes an SQLite exception cannot use a closed statement.
I had this problem too. In addition, the Ruby interpreter would segfault under certain conditions when I tried to investigate it (probably caused by SQLite).
I have a unique index declared, like so:
add_index(:table, [:column1, :column2], unique: true)
Adding the following uniqueness constraint to the model (in addition to the existing index in the migration) made the issue go away:
validates_uniqueness_of :column1, scope: :column2
Related
In Rspec, I'm creating records, e.g. let!(:user) { create(:user) }.
I can access the new user in Rspec and in the main thread of the subject. Both of these return the user:
puts User.all.to_a
puts ActiveRecord::Base.connection.exec_query('select * from users')
However, I can now longer access the user in a new thread:
Thread.new do
puts User.all.to_a
puts ActiveRecord::Base.connection.exec_query('select * from users')
end
How do I fix this? Is this just an Rspec issue? I don't think I can reproduce it in Rails console.
You have probably configured RSpec to run its test within a database transaction
Quote from the RSpec Docs:
>
When you run rails generate rspec:install, the spec/rails_helper.rb file
includes the following configuration:
RSpec.configure do |config|
config.use_transactional_fixtures = true
end
The name of this setting is a bit misleading. What it really means in Rails
is "run every test method within a transaction." In the context of rspec-rails,
it means "run every example within a transaction."
The idea is to start each example with a clean database, create whatever data
is necessary for that example, and then remove that data by simply rolling back
the transaction at the end of the example.
You might want to disable this configuration when your application tests have the requirement to support multiple threads at the same time.
But that means that your test database will not be cleared from test data automatically anymore when a test is done. Instead, you will need to delete the test data manually or use another gem (like database_cleaner) to reset the database after running tests.
So whenever I run a test, Rails seems to be attempting to insert nothing into my PostgreSQL database... causing a syntax error. What could possibly cause this? For some context, this happens with every test, regardless of how simple or complex. For example:
class PlayerTest < ActiveSupport::TestCase
test "should not save empty player" do
assert true
end
end
And then I see the following error message:
Error:
PlayerTest#test_should_not_save_empty_player:
ActiveRecord::StatementInvalid: PG::SyntaxError: ERROR: syntax error at or near ")"
LINE 1: INSERT INTO "players_venues" () VALUES ()
Also, players_venues is a many-to-many join table between players and venues. This problem does not occur outside of tests. Any help would be greatly appreciated. If any more code is required please don't hesitate to ask.
So I eventually figured this out. It turns out Rails generates empty fixtures like so:
one: {}
# column: value
#
two: {}
# column: value
When you run the tests it attempts to insert nothing! Thanks heaps to jdno for giving my a hint to look into the fixtures. Bloody champion.
My Signup database has an index on email with a unique requirement. This is great, but the problem is that I'm trying to run integration tests, and every time I go rake rspec spec/features/...rb, unless I did rake db:test:purge, rake db:test:prepare first, it runs into the unique problem and refuses to run. How do I streamline this?
From the code below, you can see that every time I'm running the test, I'm creating a set of seed data with my before(:all), but since the seed data is always the same, this is driving the uniqueness error.
I'm happy to just put this seed data elsewhere or create it some other way, as long as my test suite is still able to run using this seed data.
describe "how requests should flow" do
before(:all) do
#signup_dd = Signup.create(email:"example#example.com")
end
it "should have 2 inventories and 2 signups to start" do
Signup.count.should == 1
end
describe "request creation" do
before do
Signup.find_by_id(#signup_dd)
visit '/requests/new'
save_and_open_page
fill_in '#borrow__1', :with => 1
click_button
end
it "should affect new Requests and Borrows" do
...
end
end
end
There are two ways to fix this:
Remove the (:all) from the before block. RSpec will execute the before block for each test. It will then undo itself after each test. This is really what you want as it ensures changes made by each test do not bleed into other tests. This is usually the recommended approach.
Keep the (:all), but then add a (:after) block to undo your changes. With the :all argument, the before block is only executed once instead of every time. However, it doesn't automatically undo itself like :each, so the :after block becomes necessary. It is up to you, however, to figure out what needs to go in there. In your example, for instance, it might be:
after(:all) do
Signup.delete_all # Or whatever undoes what the before block did
end
See this blog post regarding the use of the two techniques.
when you use before(:all), you need use after(:all) to clean up the data you created in before(:all)
Similar to the problem described here:
http://rpheath.com/posts/411-how-to-use-factory-girl-with-rspec
in Short (shorten'd code):
spec_helper:
config.use_transactional_fixtures = true
config.use_instantiated_fixtures = false
factories.rb:
Factory.define :state do
f.name "NY"
end
in my spec
before(:each) do
#static_model = Factory(:state) # with validate uniqueness of state name
end
error:
duplicate entry name "NY" etc.
Question:
Shouldn't rspec clear database before each spec example and hence not throwing duplicate entry errors?
Things i think off:
do you use rake spec to run your testsuite: that builds up the database from scratch (to make sure nothing is sticking)
do you use, anywhere, a before (:all) ? Because whatever you create inside a before :all should be deleted again in a after :all or it keeps on existing.
Question: Shouldn't rspec clear database before each spec example and hence not throwing duplicate entry errors?
RSpec with DatabaseCleaner or RSpec Rails with use_transactional_fixtures will clear the DB as long as your created the data in the example itself. before :all do ... end is considered outside of the example, because the data remains untouched across multiple examples. Whatever you create in before :all you have to delete in after :all.
In order to delete whatever you create automatically use before :each do ... end. Be aware the same data will be created and removed 10 times if you have 10 examples. The difference between before :all and before :each is better explained here: rails rspec before all vs before each
Some more possible causes:
There's still a states.yml fixture sitting around
Someone played around on script/console test and forgot to clean up afterwards.
You might also find it's because you haven't wrapped the statement in:
describe "what it should do" do
#static_model = Factory(:state) # with validate uniqueness of state name
end
I discovered that was the change that solved this problem:
Why isn't factory_girl operating transactionally for me? - rows remain in database after tests
I have had similar questions about what sort of starting state one can expect when using FG and RSpec.
While I too wait for clarity, Database Cleaner could be a good fix: http://rubydoc.info/gems/database_cleaner/0.6.7/frames
hth -
Perry
When you use Factory(:state) wich is a shortcut to Factory.create(:state), factory_girl returns you a saved object.
Use Factory.build(:state) instead.
Dude maybe your yaml fixtures from regular unit tests get mixed into your rspec?
I have this Factory:
Factory.define :email_address do |e|
e.sequence(:address) { |n| "factory_#{n}#example.com" }
e.validated true
end
When I run my specs with rake spec, it works fine.
When I run autospec, it fails right away, claiming that the email address is being used twice in two different objects (there is a validation which restricts this).
Why is it behaving differently under autospec?
Sometimes, when you abort a test suite with Ctrl+C, it may leave your database dirty. As your database is dirty, creating new objects is going to have validation conflicts. Just run rake db:test:clone again and you should be fine.
I suspect what is happening is that FactoryGirl is resetting that n on each invocation from autospec, but the database hasn't been emptied.
First, to check this diagnosis, change your factory to the following:
Factory.define :email_address do |e|
e.sequence(:address) { |n| puts "Email ##{n}"; "factory_#{n}#example.com" }
e.validated true
end
If my diagnosis is correct, there are two possible fixes:
change FactoryGirl to start from an index greater than the maximum ID used. This would involve some serious hacking on Factory::Sequence - you would probably have to turn Factory::Sequence.sequences from a Hash[Symbol => Proc] to a Hash[Symbol => [Proc, Integer] that remembered the highest index it used. Indeed, that might not even work since autospec does seem to be unloading the FactoryGirl classes properly (otherwise the sequence lookup wouldn't be failing and creating a new Factory::Sequence object).
figure out why your database isn't being cleared between each autospec run. Have you checked your teardown methods? Does your testing database support transactions?