Reputation: 93
I have some strange problem with rspec and rails. So I have model test:
require 'spec_helper'
describe User do
before(:each) do
@attr = { name: "johnkowalski", fullname: "John Kowalski",
email: "[email protected]", password: "foobar" }
end
describe "Create a user" do
it "test1" do
User.create!(@attr)
end
it "test2" do
User.create!(@attr)
end
it "test3" do
User.create!(@attr)
@p = { name: "testowy", fullname: "John Kowalski",
email: "[email protected]", password: "foobar" }
a = User.new(@p)
a.should_not be_valid
end
it "test4" do
User.create!(@attr)
end
end
end
It passes without a problem but when I add some integration test like:
require 'spec_helper'
describe "Users" do
describe "signup" do
describe "failure" do
it "should not make a new user" do
lambda do
visit signup_path
fill_in "Name", with: ""
fill_in "Full name", with: ""
fill_in "Email", with: ""
fill_in "Password", with: ""
click_button
response.should render_template("users/new")
response.should have_selector("div#error_explanation")
end.should_not change(User, :count)
end
end
end
end
I have failures:
Failures:
1) Users signup failure should not make a new user
Failure/Error: visit signup_path
AbstractController::ActionNotFound:
The action 'new' could not be found for UsersController
# ./spec/requests/users_spec.rb:8:in `block (5 levels) in <top (required)>'
# ./spec/requests/users_spec.rb:7:in `block (4 levels) in <top (required)>'
2) User Create a user test4
Failure/Error: User.create!(@attr)
ActiveRecord::RecordInvalid:
Validation failed: Email has already been taken
# ./spec/models/user_spec.rb:28:in `block (3 levels) in <top (required)>'
Finished in 0.77216 seconds
5 examples, 2 failures
First failure is obvious (I have empty controller) but why second happen? Also if I make an integration test that passes, the model test also passes. I use rails 3.1.0.rc5 and rspec 2.6.4. Even when I comment the line a.should_not be_valid it also works. I don't understand it at all.
EDIT: I know that's validation problem but why test4 works in this example:
require 'spec_helper'
describe User do
before(:each) do
@attr = { name: "johnkowalski", fullname: "John Kowalski",
email: "[email protected]", password: "foobar" }
end
describe "Create a user" do
it "test1" do
User.create!(@attr)
end
it "test2" do
User.create!(@attr)
end
it "test3" do
User.create!(@attr)
@p = { name: "testowy", fullname: "John Kowalski",
email: "[email protected]", password: "foobar" }
a = User.new(@p)
a.should_not be_valid
end
it "test4" do
User.count.should == 0
end
it "test5" do
User.create!(@attr)
end
end
end
user.rb :
class User < ActiveRecord::Base
has_secure_password
email_regex = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i
validates :name, presence: true, length: { maximum: 20 },
uniqueness: { case_sensitive: false }
validates :fullname, presence: true, length: { maximum: 30 }
validates :email, format: { with: email_regex },
uniqueness: { case_sensitive: false }, length: { maximum: 30 }
validates :password, length: { in: 5..25 }
end
I found why these things happen. So when I run only model tests, I have something like that in logs:
(0.0ms) RELEASE SAVEPOINT active_record_1
(0.1ms) SELECT 1 FROM "users" WHERE LOWER("users"."name") = LOWER('testowy') LIMIT 1
(0.1ms) SELECT 1 FROM "users" WHERE LOWER("users"."email") = LOWER('[email protected]') LIMIT 1
(0.1ms) SELECT COUNT(*) FROM "users"
(0.1ms) SAVEPOINT active_record_1
(0.1ms) SELECT 1 FROM "users" WHERE LOWER("users"."name") = LOWER('johnkowalski') LIMIT 1
(0.1ms) SELECT 1 FROM "users" WHERE LOWER("users"."email") = LOWER('[email protected]') LIMIT 1
SQL (0.3ms) INSERT INTO "users" ("created_at", "email", "fullname", "name", "password_digest", "updated_at") VALUES (?, ?, ?, ?, ?, ?) [["created_at", Thu, 28 Jul 2011 13:09:48 UTC +00:00], ["email", "[email protected]"], ["fullname", "John Kowalski"], ["name", "johnkowalski"], ["password_digest", "$2a$10$e1.3fifGcs7PALH1o0GQJ.Ny/QxCS9fRxDJ6NemGkwfFJCpsD51vy"], ["updated_at", Thu, 28 Jul 2011 13:09:48 UTC +00:00]]
(0.0ms) RELEASE SAVEPOINT active_record_1
But when I run these tests and integration tests I found this in log:
(0.1ms) SELECT 1 FROM "users" WHERE LOWER("users"."email") = LOWER('[email protected]') LIMIT 1
(0.1ms) SELECT COUNT(*) FROM "users"
(0.1ms) SAVEPOINT active_record_1
(0.1ms) SELECT 1 FROM "users" WHERE LOWER("users"."name") = LOWER('johnkowalski') LIMIT 1
CACHE (0.0ms) SELECT 1 FROM "users" WHERE LOWER("users"."email") = LOWER('[email protected]') LIMIT 1
(0.0ms) ROLLBACK TO SAVEPOINT active_record_1
So the user object is bringing from memory not from database. But by default caching is disabled in test environment, and I have config.action_controller.perform_caching = false in environment/test.rb
How to disable caching in that case?
Upvotes: 1
Views: 3896
Reputation: 50057
Always hard to guess how much you know about rspec, so maybe this is all very known to you.
But: are you sure you have the following line in your spec_helper.rb
:
config.use_transactional_fixtures = true
This will make sure that after each test all created models are released. Note that everything in a before(:each)
takes place inside the transaction (and is rolled back) and what is inside a before(:all)
does not (and needs to be cleaned in a after(all)
.
So, if you use transactional fixtures, it is actually normal that User.count == 0
, since all creates are rolled back.
Also there is no use in creating a user four times, on the same level, as the result would (normally be the same).
Secondly, since you seem to be testing your validations, I would suggest taking a look at shoulda, which offers nice shortcuts. E.g. something like
it { should validate_uniqueness_of :email }
Upvotes: 2