I'm trapped on one spec in RSpec. How can I update a model's attribute to make it nil? Is better to validate under update controller spec?
Below is a sample code.
describe User do
describe ".validation" do
before(:each) do
#user = User.create!({
:username => "dexter_morgan"
})
end
...
context "given invalid attributes" do
# how can I make the username nil?
it "rejects blank username"
end
end
end
This should be enough?
describe User do
describe ".validation" do
before(:each) do
#user = User.create!({
:username => "dexter_morgan"
})
end
...
context "given invalid attributes" do
it "rejects blank username" do
#user.username = nil
#user.should_not be_valid
end
end
end
end
Related
Here is my Spec file:
require 'spec_helper'
describe User, "references" do
it { should have_and_belong_to_many(:roles) }
it { should belong_to(:account_type) }
it { should belong_to(:primary_sport).class_name("Sport") }
it { should belong_to(:school) }
it { should belong_to(:city) }
end
describe User, "factory" do
before(:each) do
#user = FactoryGirl.create(:user)
end
it "is invalid with no email" do
#user.email = nil
#user.should_not be_valid
end
it "is valid with email" do
#user.should be_valid
end
end
Factory:
FactoryGirl.define do
factory :user do
email Faker::Internet.email
password "password"
password_confirmation "password"
agreed_to_age_requirements true
end
end
The part I am trying to "test" for and not sure how to 100% is checking to make sure when a User is created that the email address is not nil.
shoulda provides validation helpers to help you test the validations.
it { should validate_presence_of(:email) }
If you want to use rspec and write your own, then
describe User do
it "should be invalid without email" do
user = FactoryGirl.build(:user, :email => nil)
#user.should_not be_valid
#user.errors.on(:email).should == 'can't be blank' #not sure about the exact message. But you will know when you run the test
end
it "should be valid with email" do
user = FactoryGirl.build(:user, :email => "user#user.com")
#user.should be_valid
end
end
When you run the test, it would read as
User
should be invalid without email
should be valid with email
Giving a good description for your test case is very important, because it kind of acts like a documentation.
I'm using rspec to test my model methods. Everything is going fine but all of a sudden factorygirl build isn't working. here's my code:
require File.dirname(__FILE__) + '/../spec_helper'
describe User do
describe 'creation' do
before(:each) do
#user = FactoryGirl.build(:user)
end
it 'is invalid without an email address' do
user = #user
user.email = nil
user.should_not be_valid
end
it 'is invalid without a password' do
user = #user
user.password = nil
user.should_not be_valid
end
it 'is invalid with an invalid email' do
user = #user
user.email = "email#invalid"
user.should_not be_valid
end
it 'is valid with a valid email and password' do
user = #user
user.should be_valid
end
end
describe "method" do
before(:each) do
#user = FactoryGirl.build(:user)
end
context "first_name" do
it "should return the user's first name" do
user = #user
user.first_name.should == "User"
end
end
context "count_of_invoices_by_year" do
# #todo not sure how to check these since .count doesn't work
it "should return the correct count of all invoices in the specified year" do
# there are two invoices in 2013
# user = #user
# user.count_of_invoices_by_year("2013", :total).should == 2
end
it "should return the correct count of paid invoices in the specified year" do
user = #user
debugger
end
it "should return the correct count of sent invoices in the specified year" do
end
end
context "sum_of_invoices_by_year" do
it "should return the sum of the grand_totals of each of the invoices in the specified year" do
# user.sum_of_invoices_by_year("2013", :total).should ==
end
it "should return the sum of the grand_totals of each of the paid invoices in the specified year" do
end
it "should return the sum of the grand_totals of each of the sent invoices in the specified year" do
end
end
end
end
all the other #user are set correctly and work but when i get down here:
it "should return the correct count of paid invoices in the specified year" do
user = #user
debugger
end
the factorygirl.build just doesn't work. I tried putting it everyone including directly in the specific test...but it just won't set to #user. what am i missing? this is the error:
NameError Exception: undefined local variable or method `user' for #<RSpec::Core::Example:0x007fc9ec7d4890>
which happens when i try to look at user because #user is nil
Turns out user isn't actually present in a test case unless you actually try to test something with it...which is very weird.
it "should return the user's first name" do
user.first_name.should == "User"
end
this has user present but this:
it "should return the user's first name" do
debugger
end
looking at the console after debugger shows user = nil here.
In my Rails 3 application, I have a RSpec spec that checks behavior of a given field (role in the User model) to guarantee that the value is within a list of valid values.
Now I am going to have the exact same spec for another field, in another model with another set of valid values. I would like to extract the common code instead of merely copying and pasting it, changing the variables.
I am wondering if this would be the case to use a shared example or other RSpec reuse technique.
Here's the relevant RSpec code:
describe "validation" do
describe "#role" do
context "with a valid role value" do
it "is valid" do
User::ROLES.each do |role|
build(:user, :role => role).should be_valid
end
end
end
context "with an empty role" do
subject { build(:user, :role => nil) }
it "is invalid" do
subject.should_not be_valid
end
it "adds an error message for the role" do
subject.save.should be_false
subject.errors.messages[:role].first.should == "can't be blank"
end
end
context "with an invalid role value" do
subject { build(:user, :role => 'unknown') }
it "is invalid" do
subject.should_not be_valid
end
it "adds an error message for the role" do
subject.save.should be_false
subject.errors.messages[:role].first.should =~ /unknown isn't a valid role/
end
end
end
end
What would be the best case to reuse this code, but extracting role (the field being verified) and User::ROLES (the collection of valid values) into parameters being passed to this code?
I think this is a perfectly reasonable use case for shared examples. e.g. something like this:
shared_examples_for "attribute in collection" do |attr_name, valid_values|
context "with a valid role value" do
it "is valid" do
valid_values.each do |role|
build(:user, attr_name => role).should be_valid
end
end
end
context "with an empty #{attr_name}" do
subject { build(:user, attr_name => nil) }
it "is invalid" do
subject.should_not be_valid
end
it "adds an error message for the #{attr_name}" do
subject.save.should be_false
subject.errors.messages[attr_name].first.should == "can't be blank"
end
end
context "with an invalid #{attr_name} value" do
subject { build(:user, attr_name => 'unknown') }
it "is invalid" do
subject.should_not be_valid
end
it "adds an error message for the #{attr_name}" do
subject.save.should be_false
subject.errors.messages[attr_name].first.should =~ /unknown isn't a valid #{attr_name}/
end
end
end
Then you can call it in your specs like this:
describe "validation" do
describe "#role" do
behaves_like "attribute in collection", :role, User::ROLES
end
end
Haven't tested this but I think it should work.
You can DRY your spec with shared_examples technic this way:
shared_examples "no role" do
it "is invalid" do
subject.should_not be_valid
end
end
context "with an empty role" do
subject { Factory.build(:user, :name => nil) }
it_behaves_like "no role"
end
context "with an invalid role value" do
subject { Factory.build(:user, :name => '') }
it_behaves_like "no role"
end
But what about your idea to DRY few specs..I think it's too much. I'm convince that spec has to be readable firstly and only then DRY'ing. If you DRY few specs, it will be probably a headache for future reading/refactoring/changing this code.
I am using Ruby on Rails 3.0.9 and RSpect 2. I am trying to refactoring some spec file in the following way (in order to test with less code similar User class object attribute values):
describe User do
let(:user1) { Factory(:user, :users_attribute_a => 'invalid_value') }
let(:user2) { Factory(:user, :users_attribute_b => 'invalid_value') }
let(:user3) { Factory(:user, :users_attribute_c => 'invalid_value') }
it "foreach user" do
[ user1, user2, user3 ].each do |user|
subject { user }
it "should be whatever"
user.should_not be_valid
...
end
end
end
end
However, if I run the above test I get the following error:
Failure/Error: it "should be whatever" do
NoMethodError:
undefined method `it' for #<RSpec::Core::ExampleGroup::Nested_1::Nested_2::Nested_2:0x00000106ccee60>
What is the problem? How can I solve that?
UPDATE after the #Emily answer.
If in the above code I use context "foreach user" do ... instead of it "foreach user" do ... I get the following error:
undefined local variable or method `user1' for #<Class:0x00000105310758> (NameError)
The problem is having one spec nested within another. You need to replace it "foreach user" with context "foreach user".
Edited to add: After some investigation, it looks like helpers set with let are only available inside of the it "should ..." block, and not in the surrounding context. I'd recommend is trying to find a different structural solution. What the best solution is will depend on what you're actually trying to test. I'm guessing what you're trying to do is make sure the user is invalid when you remove any of the required attributes. In that case, what I've done is something like this:
describe User do
let(:user_attributes){ Factory.attributes_for(:user) }
# Testing missing values aren't valid
[:name, :email, :phone].each do |required_attribute|
it "should not be valid without #{required_attribute}" do
User.new(user_attributes.except(required_attribute)).should_not be_valid
end
end
# Testing invalid values aren't valid
[[:email, 'not_an_email'], [:phone, 'not a phone']].each do |(attribute, value)|
it "should not be valid with bad value for #{attribute}" do
User.new(user_attributes.update(attribute => value)).should_not be_valid
end
end
end
If you're doing something that requires more complex differences in the instance you're creating, there may not be a clean way to do it with iteration. I don't think DRY is quite as essential in testing as it is in other parts of your code. There's nothing wrong with having three different contexts for the three user types, and a validity test in each context.
describe User do
context "with user1" do
subject{ Factory(:user, :users_attribute_a => 'invalid_value') }
it{ should_not be_valid }
end
context "with user2" do
subject{ Factory(:user, :users_attribute_b => 'invalid_value') }
it{ should_not be_valid }
end
context "with user3" do
subject{ Factory(:user, :users_attribute_c => 'invalid_value') }
it{ should_not be_valid }
end
end
You're mixing and matching all sorts of rspec stuff. Here's your stuff, fixed:
describe User do
let(:user1) { Factory(:user, :users_attribute_a => 'invalid_value') }
let(:user2) { Factory(:user, :users_attribute_b => 'invalid_value') }
let(:user3) { Factory(:user, :users_attribute_c => 'invalid_value') }
it "should not be valid" do
[ user1, user2, user3 ].each do |user|
user.should_not be_valid
end
end
end
I would do it this way:
describe User do
subject{Factory.build(:user)}
it "should not be valid with invalid users_attribute_a" do
subject.users_attribute_a = "invalid_value"
subject.should_not be_valid
end
it "should not be valid with invalid users_attribute_b" do
subject.users_attribute_b = "invalid_value"
subject.should_not be_valid
end
end
If you want to have "context", then cool, but you can't have variables before your context inside of your context.
If you want to have a specification, then have one, but you can't net "it" statements
UPDATE WITH LEAST POSSIBLE CODE
describe User do
it "should not be valid with other attributes" do
{:users_attribute_a => 'invalid_value', :users_attribute_b => 'invalid_value', :users_attribute_c => 'invalid_value'}.each do |key, value|
Factory.build(:user, key => value).should_not be_valid
end
end
end
The problem is that the helpers that are set with "let" do not exist outside of a example context.
What you're trying to do could be achieved as:
it "does something with all users" do
[user1, user2, user3] do |user|
user.valid?.should be_true
end
end
Both contexts are different
Another way it might work (haven't tried it) it's like this:
context "for all users" do
[:user1, :user2, :user3].each do |user|
it "does something" do
send(user).valid?.should be_true
end
end
end
This should work. Note how the context is written, it will make the output of tests clearer. From writing it this way it implies (to me) that you should make a test for each attribute separately, but it's your choice:
describe User do
let!(:users) {
[:users_attribute_a, :users_attribute_b, :users_attribute_c].map do |a|
Factory(:user, => 'invalid_value')
end
}
context "Given a user" do
context "With an invalid value" do
subject { users }
it { subject.all?{|user| should_not be_valid }
end
end
end
I was wondering if i could have some feedbacks with the controller spec bellow. In fact i'm new when writing specs and controller's spec are way different from model's spec ! So i'm wondering if i may not go in the wrong direction...
subjects_controller.rb
def show
#subject = Subject.find(params[:id])
if #subject.trusted?(current_user)
#messages = #subject.messages
else
#messages = #subject.messages.public
#messages = #messages + #subject.messages.where(:user_ids => current_user.id)
#messages.uniq!
end
# sort the list
#messages = #messages.sort_by(&:created_at).reverse
if !#subject.company.id == current_user.company.id
redirect_to(subjects_path, :notice => "Invalid subject")
end
end
subjects_controller_spec.rb
require 'spec_helper'
describe SubjectsController do
before(:each) do
#subject = mock_model(Subject)
end
context "for signed users" do
before(:each) do
#current_user = sign_in Factory(:user)
end
context "GET #show" do
before(:each) do
Subject.stub!(:find, #subject).and_return(#subject)
end
context "when current_user is trusted" do
before(:each) do
messages = []
company = mock_model(Company)
#subject.should_receive(:trusted?).and_return(true)
#subject.should_receive(:messages).and_return(messages)
#subject.should_receive(:company).and_return(company)
end
it "should render success" do
get :show, :id => #subject
response.should be_success
end
end
context "when current_user is not trusted" do
before(:each) do
messages = []
company = mock_model(Company)
#subject.should_receive(:trusted?).and_return(false)
#subject.should_receive(:messages).and_return(messages)
messages.should_receive(:public).and_return(messages)
#subject.should_receive(:messages).and_return(messages)
messages.should_receive(:where).and_return(messages)
#subject.should_receive(:company).and_return(company)
end
it "should render success" do
get :show, :id => #subject
response.should be_success
end
end
context "when subject's company is not equal to current_user's company" do
# I have no idea of how to implement ==
end
end
end
end
Factories.rb
Factory.define :user do |u|
u.first_name 'Test User' #
u.username 'Test User' #
u.surname 'TheTest' #
u.email 'foo#foobar.com' #
u.password 'please' #
u.confirmed_at Time.now #
end
As far as I can tell you're on the right path. The basic idea is to completely isolate your controller code from model and view in these tests. You appear to be doing that--stubbing and mocking model interaction.
Don't write RSpec controller specs at all. Use Cucumber stories instead. Much easier, and you get better coverage.