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?
test1andtest2as your test names! The whole point of the rSpec DSL is that you should describe your tests in something resembling natural language. Instead ofit "test1"you should always useit "should be invalid"or some other actually descriptive name.