I'm trying to write a spec for a Student class. So far it's simple; it only has a first name and a last name. I need to make sure both those fields aren't blank. I have this so far:
describe Student do
context "is valid" do
before do
#student = Student.new
end
it "should have a first name" do
expect(student).not_to be_valid
end
it "should have a last name" do
expect(student).not_to be_valid
end
end
end
When I run the tests it says student is an undefined local variable for both of them. Why does the before not work?
Because #student is an instance variable, and RSpec does not create attributes automagically. If you want to use student instead of #student, use let:
describe Student do
context "is valid" do
let(:student) { Student.new }
it "should have a first name" do
expect(student).not_to be_valid
end
it "should have a last name" do
expect(student).not_to be_valid
end
end
end
Related documentation: http://www.betterspecs.org/#let
Related
My application manages BusinessFlows, which can only be created within a parent BusinessArea. The 'new' method in BusinessFlows controller is:
def new
#business_area = BusinessArea.find(params[:business_area_id])
#business_flow = BusinessFlow.new
end
The Factory used for testing works fine, and creates the parent BusinessArea, and the the BusinessFlow as expected:
FactoryBot.define do
factory :business_flow do
association :parent, factory: :business_area
playground_id {0}
name {"Test Business Flow"}
code {Faker::Code.unique.rut}
description {"This is a test Business Flow used for unit testing"}
created_by {"Fred"}
updated_by {"Fred"}
owner_id {0}
responsible_id {0}
deputy_id {0}
organisation_id {0}
status_id {0}
end
end
But when it comes to test if the 'new' view can be displayed, the controller raises an error due to missing params[:business_area_id]:
ActiveRecord::RecordNotFound:
Couldn't find BusinessArea without an ID
Here is the feature test script:
require 'rails_helper'
RSpec.describe BusinessFlow, type: :request do
include Warden::Test::Helpers
describe "Business Flows pages: " do
let(:bf) {FactoryBot.create(:business_flow)}
context "when not signed in " do
it "should propose to log in when requesting index view" do
get business_flows_path
follow_redirect!
expect(response.body).to include('Sign in')
end
it "should propose to log in when requesting new view" do
get new_business_flow_path
follow_redirect!
expect(response.body).to include('Sign in')
end
it "should propose to log in when requesting edit view" do
get edit_business_flow_path(bf)
follow_redirect!
expect(response.body).to include('Sign in')
end
it "should propose to log in when requesting show view" do
get business_flow_path(bf)
follow_redirect!
expect(response.body).to include('Sign in')
end
end
context "when signed in" do
before do
get "/users/sign_in"
test_user = FactoryBot.create(:user)
login_as test_user, scope: :user
end
it "should display index" do
get business_flows_path
expect(response).to render_template(:index)
end
it "should display new view" do
get new_business_flow_path(bf.parent.id)
expect(response).to render_template(:_form)
end
it "should display edit view" do
get edit_business_flow_path(bf)
expect(response).to render_template(:_form)
end
it "should display show view" do
get business_flow_path(bf)
expect(response).to render_template(:show)
end
end
end
end
Only the 'new' method test in the context 'when signed in' fails.
Can you please help me solving this?
Thanks a lot!
it "should display new view" do
get new_business_flow_path(business_area_id: bf.parent)
expect(response).to render_template(:_form)
end
Rails thinks the id you're passing is part of the business flow path (and it isn't), it needs to be passed as a param I think.
I am doing the thoughtbot intro to testing program. Im not sure how to test for what they want.
Below is my test.
require "rails_helper"
describe PeopleController do
describe "#create" do
context "when person is valid" do
it "redirects to #show" do
post :create, FactoryGirl.build_stubbed(:person)
expect(response).to redirect_to(show_people_path)
end
end
context "when person is invalid" do
it "redirects to #new" do
pending "create this test"
end
end
end
end
I am of course using factory girl. I have tried several methods. I really don't know hoe to test this controller.
Any insights would be great.
I would create an 'invalid' person using the FactoryGirl, and send it as a parameter to the post :create.
To create an invalid person record, why don't you use nested factories in FactoryGirl? Depending on the validation in your model, you can simply do something like:
spec/factories/person.rb
FactoryGirl.define do
factory :person do
...
factory :invalid_person do
...
email nil
...
end
end
end
in your test
context "when person is invalid" do
it "redirects to #new" do
post :create, FactoryGirl.build_stubbed(:invalid_person)
expect(response).to redirect_to action: :new
end
end
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'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