Devise/Rspec - Tested a user creation with (maybe) missing attributes (got true..) - ruby-on-rails

I am testing Devise with Rspec using Micheal Hartl source code (railstutorial)
Whereas the confirmable module is enabled, I don't understand why this test pass:
spec/models/user_spec.rb
require 'spec_helper'
describe User do
before(:each) do
#attr = { :username => "ExampleUser",
:email => "user#example.com",
:password => 'test1234',
}
end
it "should create a new instance given valid attributes" do
User.create!(#attr)
end
end
Basically, I want to be sure of this code does, it tests the creation on the user, not this validation (cause the user has not confirmed yet and the test returns true) ? This is right?
Moreover, I didn't provide attribute for password confirmation, and the user is still created!
Is this mean that in the :validatable module there is not (?):
validates :password, :confirmation => true
Thanks to get you view on this!

one problem is the trailing comma at the end of your each block. second, you are not asserting anything in your test to pass or fail the test, though you are probably erroring out at this point, which is why you are saying it didnt pass.
you can try assigning the user object to a variable:
it "should create a new instance given valid attributes" do
#user = User.new(#attr)
#user.should be_valid #=> new will let you know if its valid or not
#user.save.should be_true #=> another possible assertion to pass/fail the test
# debug message to give you back why it failed
puts #user.errors.full_messages.to_sentence
end

Related

Rspec not logging me in

I was looking at this answer to see how to test a session controller and wrote something like this:
require 'spec_helper'
describe SessionsController do
context "We should login to the system and create a session" do
let :credentials do
{:user_name => "MyString", :password => "someSimpleP{ass}"}
end
let :user do
FactoryGirl.create(:user, credentials)
end
before :each do
post :create , credentials
end
it "should create a session" do
puts user.inspect
puts session[:user_id]
#session[:user_id].should == user.id
end
end
end
Based on that I created a factory girl user:
FactoryGirl.define do
factory :user, :class => 'User' do
name "sample_user"
email "MyString#gmail.com"
user_name "MyString"
password "someSimpleP{ass}"
end
end
Now it all works - exceot for the before :each do statement - it never "logs" the "user" in - thus I cannot test the controllers functionality of, is a session properly created?
Now most would say, use capybara and test it through that way - but that's wrong, IMO - sure if I'm doing front end testing that would work, but I'm testing controller based logic. Can some one tell me why this isn't working? routing works fine.
My puts session[:user_id] is coming up nil, when it shouldn't
let is lazily evaluated, even for the before clause, so the user has not been created as of the time you do the post to login. If you change to using let!, you'll avoid this problem.
You misunderstood SessionsController and RegistrationsController.
A Session is for an user who has already registered, not for creating an user. #create in SessionController means to create a session, not an user.
RegistrationController is for creating user with full details including password_confirmation.
To test SessionsController, you need to create a valid user in FactoryGirl at first, then use his credentials say email and password to sign in.

Make Rails Class Attribute Inaccessible - Rails Tutorial Chapter 9, Exercise 1

I'm working through Michael Hartl's Rails Tutorial. I've come to Chapter 9, Exercise 1. It asks you to add a test to verify that the admin attribute of the User class is not accessible. Here's the User class with irrelevant portions commented out:
class User < ActiveRecord::Base
attr_accessible :name, :email, :password, :password_confirmation
attr_protected :admin
# before_save methods
# validations
# private methods
end
And here's the test I'm using to validate that the admin attribute is not accessible.
describe User do
before do
#user = User.new(
name: "Example User",
email: "user#example.com",
password: "foobar123",
password_confirmation: "foobar123")
end
subject { #user }
describe "accessible attributes" do
it "should not allow access to admin" do
expect do
#user.admin = true
end.should raise_error(ActiveModel::MassAssignmentSecurity::Error)
end
end
end
The test fails. It says no errors were raised, in spite of the fact that the admin attribute is protected. How can I get the test to pass?
From the Ruby documentation:
Mass assignment security provides an interface for protecting attributes from end-user assignment.
http://api.rubyonrails.org/classes/ActiveModel/MassAssignmentSecurity/ClassMethods.html
Try this code instead
describe "accesible attributes" do
it "should not allow access to admin" do
expect do
User.new(admin: true)
end.should raise_error(ActiveModel::MassAssignmentSecurity::Error)
end
end
As Rails docs claim about attr_protected
Attributes named in this macro are protected from mass-assignment, such as new(attributes), update_attributes(attributes), or attributes=(attributes).
So you can change field manually. 'attr_protected' is only about mass-assignment.
This only works for mass assignments like setting the field from a form submit. Try something like this:
#user.update_attrtibutes(:admin => true)
#user.admin.should be_false
#agaved. This answer may come late and you may already have the answer but I wanted to answer your question, it may help somebody else.
The best way to understand how update_attributes differs from direct assignment
#user.admin = true is to try and do it in your console. If you are following Hartl's tutorial, try the following:
#user = User.first
#user.admin?
=> true
#user.admin = false
=> false
Direct assignment manages to change the value of the attribute admin for user from true to false without raising a Mass Assignment Error. This is because Mass Assignment Errors are raised when you call update_attributes or create a new user User.new using an attribute that is not accessible. In other words, Rails raises mass assignment errors when a user tries to update (attribute_update) or create User.new(admin: true) a new user with attributes that are not accessible to her. In the above case, direct assignment is not using the create or update methods of the user controller.
They are very similar pieces of code since you can use direct assignment to force a change in the admin attribute in the above case using #user.save!(validate: false) directly in IRB but as I said above this does not use the create or update method of your user controller and, hence, it will not throw the error.
I hope that helps, this helped me.
[Spoiler alert: If you are trying to solve the exercises in Hartl's book on your own, I'm pretty sure I'm about to give the answer away. Even though the answer that has been accepted is interesting information, I don't believe it was what Hartl had in mind as that would require knowledge the book has not covered and also does not relate it specifically to updates via web action or use the test he provides.]
I think you might be thinking this exercise is a lot harder than it actually is, if I got it right. First of all, you have misunderstood the hint:
Hint: Your first step should be to add admin to the list of permitted parameters in user_params.
It does not say to change its attr declaration in the class. It says to modify the helper function user_params. So I added it to the list in users_controller.rb:
def user_params
params.require(:user).permit(:name, :email, :password,
:password_confirmation, :admin)
end
Next, I copied the code in Listing 9.48 to the indicated place in spec/requests/user_pages_spec.rb:
require 'spec_helper'
describe "User pages" do
.
.
.
describe "edit" do
.
.
.
describe "forbidden attributes" do
let(:params) do
{ user: { admin: true, password: user.password,
password_confirmation: user.password } }
end
before do
sign_in user, no_capybara: true
patch user_path(user), params
end
specify { expect(user.reload).not_to be_admin }
end
end
end
The test then fails, showing that it is possible to pass in an admin parameter and thus change a normal user to an admin, which is not what you would want to allow:
$ rspec spec
.....................[edited out dots].................................F
Failures:
1) User pages edit forbidden attributes
Failure/Error: specify { expect(user.reload).not_to be_admin }
expected admin? to return false, got true
# ./spec/requests/user_pages_spec.rb:180:in `block (4 levels) in <top (required)>'
Finished in 4.15 seconds
91 examples, 1 failure
Failed examples:
rspec ./spec/requests/user_pages_spec.rb:180 # User pages edit forbidden attributes
Then, to make it impossible to pass in an admin value via a web action, I simply removed :admin from the list of acceptable user_params, undoing the first change:
def user_params
params.require(:user).permit(:name, :email, :password,
:password_confirmation)
end
Now the attempt to patch the user with a new admin value fails... and the test for it succeeds, verifying "that the admin attribute isn’t editable through the web."
$ rspec spec
...........................................................................................
Finished in 4.2 seconds
91 examples, 0 failures
Following the hint, I first added :admin to attr_accessible in app/models/user.rb to start with a red.
I then added the test:
describe "admin attribute" do
it "should not be accessible" do
expect do
#user.update_attributes(:admin => true)
end.to raise_error(ActiveModel::MassAssignmentSecurity::Error)
end
end
to the spec and got a red.
Removing :admin from user.rb I get a green. So far so good.
What puzzles me is why I should use the sintax:
#user.update_attributes(:admin => true)
instead of #user.admin = true (I checked and in this case it doesn't work).

Factory Girl failing Rspec validation tests

I've been trying to get a grasp on writing tests, but having a lot of trouble as the tests never seem to validate the way I want them to. In particular, I've been trying to use Factory Girl as opposed to fixtures - as suggested by a recent Railscasts and other advice I've seen on the net - due to the benefits it professes, and that hasn't worked out.
For example, here is a simple Rspec test for a user model, testing to make sure a username is present...
describe User do
it "should not be valid without a username" do
user = FactoryGirl.create(:user, :username => "", :password => "secret")
user.should_not be_valid
end
end
And my factories.rb file, if it helps...
FactoryGirl.define do
factory :user do
sequence(:username) { |n| "registered-#{n}" }
password "foobar"
end
end
When I run 'rake spec,' it tells me...
1) User should not be valid without a username
Failure/Error: user = FactoryGirl.create(:user, :username => "", :password => "secret")
ActiveRecord::RecordInvalid:
Validation failed: Username can't be blank
Ummm...that's the POINT. If I specified that the user should NOT be valid, shouldn't this test actually pass?
If I replace the Factory Girl line and set the user in the test with something like 'user = User.new(:username => "", :password => "secret")', to no surprise the test passes fine. So why is Factory Girl not working right?
You should use build like in the following:
user = Factory.build(:user, :username=>"foo")
Because using the method you're using will try to create a record. See docs for further information.

Rails 3.1, RSpec: testing model validations

I have started my journey with TDD in Rails and have run into a small issue regarding tests for model validations that I can't seem to find a solution to. Let's say I have a User model,
class User < ActiveRecord::Base
validates :username, :presence => true
end
and a simple test
it "should require a username" do
User.new(:username => "").should_not be_valid
end
This correctly tests the presence validation, but what if I want to be more specific? For example, testing full_messages on the errors object..
it "should require a username" do
user = User.create(:username => "")
user.errors[:username].should ~= /can't be blank/
end
My concern about the initial attempt (using should_not be_valid) is that RSpec won't produce a descriptive error message. It simply says "expected valid? to return false, got true." However, the second test example has a minor drawback: it uses the create method instead of the new method in order to get at the errors object.
I would like my tests to be more specific about what they're testing, but at the same time not have to touch a database.
Anyone have any input?
CONGRATULATIONS on you endeavor into TDD with ROR I promise once you get going you will not look back.
The simplest quick and dirty solution will be to generate a new valid model before each of your tests like this:
before(:each) do
#user = User.new
#user.username = "a valid username"
end
BUT what I suggest is you set up factories for all your models that will generate a valid model for you automatically and then you can muddle with individual attributes and see if your validation. I like to use FactoryGirl for this:
Basically once you get set up your test would look something like this:
it "should have valid factory" do
FactoryGirl.build(:user).should be_valid
end
it "should require a username" do
FactoryGirl.build(:user, :username => "").should_not be_valid
end
Here is a good railscast that explains it all better than me:
UPDATE: As of version 3.0 the syntax for factory girl has changed. I have amended my sample code to reflect this.
An easier way to test model validations (and a lot more of active-record) is to use a gem like shoulda or remarkable.
They will allow to the test as follows:
describe User
it { should validate_presence_of :name }
end
Try this:
it "should require a username" do
user = User.create(:username => "")
user.valid?
user.errors.should have_key(:username)
end
in new version rspec, you should use expect instead should, otherwise you'll get warning:
it "should have valid factory" do
expect(FactoryGirl.build(:user)).to be_valid
end
it "should require a username" do
expect(FactoryGirl.build(:user, :username => "")).not_to be_valid
end
I have traditionally handled error content specs in feature or request specs. So, for instance, I have a similar spec which I'll condense below:
Feature Spec Example
before(:each) { visit_order_path }
scenario 'with invalid (empty) description' , :js => :true do
add_empty_task #this line is defined in my spec_helper
expect(page).to have_content("can't be blank")
So then, I have my model spec testing whether something is valid, but then my feature spec which tests the exact output of the error message. FYI, these feature specs require Capybara which can be found here.
Like #nathanvda said, I would take advantage of Thoughtbot's Shoulda Matchers gem. With that rocking, you can write your test in the following manner as to test for presence, as well as any custom error message.
RSpec.describe User do
describe 'User validations' do
let(:message) { "I pitty da foo who dont enter a name" }
it 'validates presence and message' do
is_expected.to validate_presence_of(:name).
with_message message
end
# shorthand syntax:
it { is_expected.to validate_presence_of(:name).with_message message }
end
end
A little late to the party here, but if you don't want to add shoulda matchers, this should work with rspec-rails and factorybot:
# ./spec/factories/user.rb
FactoryBot.define do
factory :user do
sequence(:username) { |n| "user_#{n}" }
end
end
# ./spec/models/user_spec.rb
describe User, type: :model do
context 'without a username' do
let(:user) { create :user, username: nil }
it "should NOT be valid with a username error" do
expect(user).not_to be_valid
expect(user.errors).to have_key(:username)
end
end
end

Why is my rspec test failing?

Here's the test:
describe "admin attribute" do
before(:each) do
#user = User.create!(#attr)
end
it "should respond to admin" do
#user.should respond_to(:admin)
end
it "should not be an admin by default" do
#user.should_not be_admin
end
it "should be convertible to an admin" do
#user.toggle!(:admin)
#user.should be_admin
end
end
Here's the error:
1) User password encryption admin attribute should respond to admin
Failure/Error: #user = User.create!(#attr)
ActiveRecord::RecordInvalid:
Validation failed: Email has already been taken
# ./spec/models/user_spec.rb:128
I'm thinking the error might be somewhere in my data populator code:
require 'faker'
namespace :db do
desc "Fill database with sample data"
task :populate => :environment do
Rake::Task['db:reset'].invoke
admin = User.create!(:name => "Example User",
:email => "example#railstutorial.org",
:password => "foobar",
:password_confirmation => "foobar")
admin.toggle!(:admin)
99.times do |n|
name = Faker::Name.name
email = "example-#{n+1}#railstutorial.org"
password = "password"
User.create!(:name => name,
:email => email,
:password => password,
:password_confirmation => password)
end
end
end
Please let me know if I should reproduce any more of my code.
UPDATE: Here's where #attr is defined, at the top of the user_spec.rb file:
require 'spec_helper'
describe User do
before(:each) do
#attr = {
:name => "Example User",
:email => "user#example.com",
:password => "foobar",
:password_confirmation => "foobar"
}
end
Check to be sure that there isn't a block further up your user_spec.rb that is calling User.create in a before(:each) block with the same email address. If your blocks are nested incorrectly, you'll get this error. For example, in the Rails tutorial, it's easy to accidentally nest your describe "admin attribute" inside your describe "password encryption" block, which uses the same before(:each) code.
Try checking for existing users in the before block:
before(:each) do
User.count.should == 0
#user = User.create!(#attr)
end
If that fails, then another user exists with the same email. This could be because another before block created a user with the same attributes, or that the test database was not correctly cleaned out after a failure. For the latter case, try running rake db:test:prepare, and then run the spec again.
before( :each ) is going to create a new user object from #attr. So if #attr isn't changing the values for its fields, and you have validations turned on to prevent duplicate, then on your 2nd test, the user object you created in the first test will collide with the one you are trying to create in the 2nd test.
There are other ways to go about testing your model without the database. For example, you can use test doubles to create and setup objects with exactly the data you want and then run your test to see if it behaves correctly. There is a [great book on RSpec, Cucumber and BDD] that could be a great source.
Edit: My apologies, I was confusing before(:each) with before(:all).
This does not seems to be ideal way of setting up test data. ie, using a rake task to populate the database.
A more standard unit testing and Rails practice would be to use both factory_girl or a test_fixture and transactional test fixture or database_cleaner gem.
Read a little bit about those, and they should be straight forward to use. They ensure, that each of your rspec test runs in isolation even when you run all of them together. That way, each test data for one test will not affect the other one.

Resources