Hartl Rails Tutorial: upcase-ing email address in user_test - ruby-on-rails

I'm about halfway through the Rails tutorial (excellent, btw), and have a little question. Is there a reason that this test uses duplicate_user.email = #user.email.upcase and not the more succinct duplicate_user.email.upcase ?
Here is the full test.
test "email addresses should be unique" do
duplicate_user = #user.dup
duplicate_user.email = #user.email.upcase
#user.save
assert_not duplicate_user.valid?
end
As far as I can tell, the test performs correctly doing it either way.

I'm not familiar with the tutorial but you seem to be asking why the test is not written as:
test "email addresses should be unique" do
duplicate_user = #user.dup
duplicate_user.email.upcase
#user.save
assert_not duplicate_user.valid?
end
In this case, the line duplicate_user.email.upcase will just return the upcased email. It will not affect the attribute on the object. The test still passes because the duplicate_user has the same email address as the original #user, satisfying the test spec that email addresses should be unique.
What the test is actually doing is verifying that the code recognises an upcased email to be the same as a downcased email. In such a case, the original line in the test has the effect of assigning to the email attribute of the duplicate_user an upcased version of the email address.

Related

What is the RSpec syntax equivalent for assert (minitest)

I am following Michael Hartl's Ruby on Rails tutorial. I observe that he used minitest to run his tests. I use RSpec. Below is a line of code from the tutorial I want to replicate with RSpec
test "email validation should accept valid addresses" do
valid_addresses = %w[user#example.com USER#foo.COM A_US-ER#foo.bar.org
first.last#foo.jp alice+bob#baz.cn]
valid_addresses.each do |valid_address|
#user.email = valid_address
assert #user.valid?, "#{valid_address.inspect} should be valid"
end
end
This is how I will do this in with RSpec.
it "email validation should accept valid addresses" do
valid_addresses = ["user#example.com", "USER#foo.COM", "A_US-ER#foo.bar.org",
"first.last#foo.jp", "alice+bob#baz.cn"]
valid_addresses.each do |valid_address|
user.email = valid_address
expect(#user.valid?).to be true, "#{valid_address.inspect} is not correct"
end
end
However, I get Argument error when I run this test. It appears RSpec expects only one argument in test cases.
RSpec thinks that you're passing a single argument to the to method and two arguments to the be method, and hence it's giving you an error. It's seeing your statement as:
expect(#user.valid?).to(be(true, "#{valid_address.inspect} is not correct"))
So, you can change your spec to be the following, and it should work:
expect(#user.valid?).to be(true), "#{valid_address.inspect} is not correct"
Here, RSpec is seeing two arguments being passed to the to method, which is valid.
What exactly is the error message you are getting. It looks like you have a typo. Should
user.email = valid_address
be
#user.email = valid address
You're providing 2 arguments and the expect statement takes one.
expect(#user.valid?).to be true

How is user saved in user_spec.rb test?

Mike Hartl's book gives the below for Listing 6.17 for a test for the rejection of duplicate email addresses, insensitive to case.
The call to User.new creates an in memory user but nowhere is #user.save called to save it to the database.
Running the test, it correctly rejects the duplicate address. But I can't figure out how it works. Only user_with_same_email is saved to the database not #user. So how could the validity test detect the duplicate without #user saved?
require 'spec_helper'
describe User do
before do
#user = User.new(name: "Example User", email: "user#example.com")
end
subject { #user }
.
.
.
describe "when email address is already taken" do
before do
user_with_same_email = #user.dup
user_with_same_email.email = #user.email.upcase
user_with_same_email.save
end
it { should_not be_valid }
end
end
user_with_same_email is actually saved in the database.
Notice that subject is #user.
subject { #user }
Hence, example it { should_not be_valid } is executed on implied subject which is #user.
#user.valid? returns false as you have put uniqueness constraint on the email address and there is already a record with same email address in your database(recall user_with_same_email that was saved).
So, your test example passes as #user is not valid.
The object #user need not be saved to run the validity check. The rspec expression it { should_not be_valid } calls #user.valid? under the hood. This method can be run on an ActiveRecord instance whether or not it has been saved.

RSpec failling in email should not be valid

I'm new to all this rails/rspec and so on, so im trying to follow some tests that I had found in the project that i'm working and make sense, they were generated by scaffold but the app is very different and no tests were updated so im re-doing every test.
The problem is when I try to validate that invalid emails should make the user invalid it fails.
I know that it's ok because I already done tests and they pass without no problem.
It is possible that I'm looking at this the wrong way but ...
User_spec
describe "when email format is invalid" do
it "should be invalid" do
addresses = %w[user#foo,com user_at_foo.org example.user#foo.
foo#bar_baz.com foo#bar+baz.com]
addresses.each do |invalid_address|
#user.email = invalid_address
#user.should_not be_valid
end
end
end
User_test
test "fail save new user with an corrupt email" do
user = User.new
user.name = 'Someone Name'
user.password = '12345678'
user.email = 'Someone Name#gmail..com'
assert !user.save , "User was saved with an corrupt email"
end
The validation is done by devise and the failure message is "expected valid? to return false, got true"
---- edit ---
My user setup
...
describe User do
before :each do
#user = User.new(name: "Name of Names", email:"someone1#somewhere.com",password: "foobarfoobar", password_confirmation: "foobarfoobar" )
end
...
You could do something along the lines of this:
context "validations" do
describe "email" do
subject { FactoryGirl.create(:user, email: email) }
context "with an valid address" do
let(:email) { "valid#email.com" }
it { should be_valid }
end
context "with an invalid email address" do
let(:email) { "invalid.email.com" }
it { should_not be_valid }
end
end
end
So I'm assuming you are using FactoryGirl to create an valid user object. Just create new tests per invalid email, this way you can see which tests are passing and which aren't. Then you could TDD your way to green.

Rails Tutorial Rspec misunderstanding

The listing 6.20 of Michael Hartl's rails tutorial shows the following code:
before do
#user = User.new(name: "Example User", email: "user#example.com")
end
.
.
.
describe "when email address is already taken" do
before do
user_with_same_email = #user.dup
user_with_same_email.email = #user.email.upcase
user_with_same_email.save
end
it { should_not be_valid }
end
I am having trouble grasping this concept because #user.dup returns a representation of the exact same object, which is copied over to user_with_same email, but #user was never saved into the database anywhere in the file. Therefore, the user_with_same_email.save test should be valid every time. However, the test passes. Someone please explain this... is there an implicit database save on #user = User.new(...)? I know if it was User.create(...) there would be a save, but not for the new method. Thanks!
You're not missing an implicit save.
user_with_same_email does save correctly (personally I would always use save! to be sure it was not failing silently)
What the spec is speccing is that the subject (ie #user) cannot be saved, because of the existance of a row in the database with the same email.

Why does my test fail when I use FactoryGirl in this manner?

I'm testing my User model and studying how FactoryGirl works. When I do this in my user_spec.rb:
before(:each) do
#user = User.new(username: 'ExampleUser', email: 'user#example.com', timezone: 'Eastern Time (US & Canada)', password: 'example')
end
Everything passes, but if I do:
before(:each) do
#user = FactoryGirl.create(:user)
end
It fails the test to see if the user's username and email are taken already.
1) User when username is already taken
Failure/Error: it { should_not be_valid }
expected valid? to return false, got true
# ./spec/models/user_spec.rb:151:in `block (3 levels) in <top (required)>'
2) User when email address is already taken
Failure/Error: it { should_not be_valid }
expected valid? to return false, got true
# ./spec/models/user_spec.rb:142:in `block (3 levels) in <top (required)>'
Finished in 1.8 seconds
29 examples, 2 failures
These are the test:
describe 'when email address is already taken' do
before do
user_with_same_email = #user.dup
user_with_same_email.email = #user.email.upcase
user_with_same_email.save
end
it { should_not be_valid }
end
describe 'when username is already taken' do
before do
user_with_same_username = #user.dup
user_with_same_username.username = #user.username.upcase
user_with_same_username.save
end
it { should_not be_valid }
end
Can someone explain? I thought FactoryGirl was suppose to let me use it like User.new, my first example which works.
FactoryGirl.create actually creates the record, whereas User.new only instantiates the model but does not actually save the record.
If you want to only instantiate the model, you should use FactoryGirl.build:
before(:each) do
#user = FactoryGirl.build(:user)
end
See the documentation for details.
So what I think is happening with your current code is that when you create the user with FactoryGirl.create, it actually saves the record with no validation issues (since the duplicate has not been created yet). When you save the user with the same email with user_with_same_email.save, it does not actually save that user but you don't see that. Then when you check if the original user is valid, it says yes because you already saved it before trying (and failing) to create the duplicate.
Make sense? Anyway just switch to FactoryGirl.build and both tests should pass.
Generally when testing a field, using Factory Girl, that has validates_uniqueness_of it's best to use a sequence.
When using a sequence, every time you create a record with FactoryGirl.create(:user), the username will always be unique. This allows you to work with "real" records in your database without having to manually correct for conflicting values.
factory :user do
sequence :username do |n}
"user_#{n}"
end
end
Note: I don't like the idea of testing records that haven't been added to the database. I can't think of any solid reason why it would be a problem. The only problem I can think of is the fact that you won't be able to test associations.
Something else that I keep noticing with your questions is you use a before block and create instance variables. In RSpec there is a method called let will create the variable when it is needed.
This would make your user_spec.rb file work like so.
describe User do
let(:user) { create(:user, :first_name => "John", :last_name => "Doe") }
it "should get full name" do
user.full_name.should == "John Doe"
end
end
let also has a bang method that will create the variable whether it is used in the it block or not.

Resources