Sometimes new (very DRY) rspec syntax makes me crazy...
Rspec v 2.14.1
describe "POST create" do
subject { post :create, contractor: valid_params }
context "by user" do
before { sign_in #legal.user }
it "contractor successful created" do
expect { subject }.to redirect_to(contractor_path(assigns(:contractor).id))
I have error & question here:
NoMethodError: # :contractor variable not defined
undefined method `id' for nil:NilClass
It seems that expect take an operator before controller method post executes, because I try to raise this method.
My code:
def create
#contractor = Contractor.restrict!(current_accreditation).new(permitted_params) # TODO move to the IR::Base
if #contractor.save
current_accreditation = #contractor.create_legal!(user: current_user) # TODO legal create
redirect_to(#contractor)
else
render(:new)
end
end
Secondly, why I have an error when try
expect(subject).to ...
Why {} works, but () no? In relish docs this method work great: https://www.relishapp.com/rspec/rspec-rails/docs/matchers/redirect-to-matcher
Kinda unrelated but I've found the following helpful:
Use expect {} when you want to test before/after of whatever's in the block. eg. expect { subject } to change(User, :count) - you want to check the count before, then after, to work out the difference and see if it actually changed.
Use expect () to verify the outcome of a block, eg. that a redirect occurred, or something got assigned, etc.
Your test with the assigns(:contractor) doesn't work because you're using the {} notation - so it's trying to work out the assigns(:contractor).id both before and after evaluating the subject (and of course, before the subject, it doesn't exist).
Related
Hi i have written Rspec to validate a body of message\n
describe "validate_msm" do
let!(:user) { FactoryGirl.create(:user) }
it "should contains welcome user" do
body = user.send_sms("909090990")
expect(body).to include("welcome")
end
end
as send_sms will call the send method which i have mentioned in let!
def send_sms
...
..
body="welcome user"
Message.send(contact, body)
end
so how to check with the body content is equal to welcome user ,
as send_sms doesn't return anything, so how to checks the value present in the body variable in rspec
You don't. Or rather you don't that easily. Most of the libraries like this should come together with test adapters and helpers to make such testing possible. If this one does not, you can only test that the message has been sent with correct arguments:
it "should contains welcome user" do
allow(Message).to receive(:send)
user.send_sms("909090990")
expect(Message).to have_received(:send) do |_contact, body|
expect(body).to include "welcome"
end
end
I got a method to update the person by id:
def update_person(id)
handle_exceptions do
person = Person.find(id)
#...other
end
end
When this id doesn't exist, the handle_exception should be called. But how could I test it? The test I wrote is:
context 'not found the proposals' do
subject {controller.send(:update_person, 3)}
before do
allow(Person).to receive(:find).and_raise(ActiveRecord::RecordNotFound)
allow(subject).to receive(:handle_exceptions)
end
it 'calls handle_exceptions' do
expect(subject).to have_received(:handle_exceptions)
end
end
But it not works, I got a failure said:
Failure/Error: expect(subject).to have_received(:handle_exceptions)
({:message=>"Not Found", :status=>:not_found}).handle_exceptions(*(any args))
expected: 1 time with any arguments
received: 0 times with any arguments
The handle_exceptions method is
def handle_exceptions
yield
rescue ActiveRecord::RecordNotFound => e
flash[:warning] = 'no record found'
Rails.logger.error message: e.message, exception: e
#error_data = { message: 'no record found', status: :not_found }
end
The problem is that you are calling the method under test in the subject block.
subject {controller.send(:update_person, 3)}
This is actually called before the example runs and before the before block.
context 'not found the proposals' do
before do
allow(subject).to receive(:handle_exceptions)
end
it 'calls handle_exceptions' do
controller.send(:update_person, "NOT A VALID ID")
expect(subject).to have_received(:handle_exceptions)
end
end
But as far as tests go this one is not good. You're testing the implementation of update_person and not the actual behavior. And you're calling the method with update_person.send(:update_person, 3) presumably to test a private method.
You should instead test that your controller returns a 404 response code when try to update with an invalid id. Also why you insist on stubbing Person.find is a mystery since you can trigger the exception by just passing an invalid id. Only stub when you actually have to.
After couple days working, I realized the reason I'm confused about it is I didn't figure out about 'who called this function', and I think it's the most important thing to know before test it. For the method like this:
class User::Controller
def methodA
methodB
end
def methodB
// ...
end
The mistake that I made is I thought the methodB is called by methods, but it's not. It's called by the controller, and that's the reason that I can't make the test works. There's so many things need to learn, and I hope there's one day that I won't have a mistake like this and be able to help others.
I want to test that a class receives a class-method call in RSpec:
describe MyObject do
it "should create a new user" do
expect(User).to receive(:new)
MyObject.new.doit
end
end
class MyObject
def doit
u = User.new
u.save
end
end
The problem is that the expectation does not halt execution. It simply stubs the class method .doit and continues execution.
The effect of the expectation is to ensure that User.new returns nil. So when we get to the next line which is User.save it then fails because there is no user object to call .save on.
I would like execution to halt as soon as the RSpec expectation has been satisfied - how can I do that?
nb
This is just an illustrative example - while an expect to change would work for User.new, it's not this actual code that I need to test
There is a great method for this and_call_original:
expect(User).to receive(:new).and_call_original
based on your test description, you're testing that a record was created, in those cases I would suggest you to do this:
expect {
MyObject.new.doit
}.to change{User.count}
or if you want to make sure it only created one:
expect {
MyObject.new.doit
}.to change{User.count}.by(1)
I have finished Hartl's Rails Tutorial and one area of confusion still reigns: when do I use #variable, when should I use :variable, and when is just variable correct?
Here is some example code I took from the tutorial:
describe "micropost associations" do
before { #user.save }
let!(:older_micropost) do
FactoryGirl.create(:micropost, user: #user, created_at: 1.day.ago)
end
let!(:newer_micropost) do
FactoryGirl.create(:micropost, user: #user, created_at: 1.hour.ago)
end
.
.
.
it "should destroy associated microposts" do
microposts = #user.microposts.dup
#user.destroy
microposts.should_not be_empty
microposts.each do |micropost|
Micropost.find_by_id(micropost.id).should be_nil
end
end
end
.
.
.
end
As compared to:
describe Micropost do
let(:user) { FactoryGirl.create(:user) }
before { #micropost = user.microposts.build(content: "Lorem ipsum") }
Here are some more specific questions that this (and other code) raises for me:
Does #user need the # in the first snippet because it's the subject or..?
Do I always declare new variables using :? (Actually I'm fairly sure this isn't the case but I don't understand there whys and wherefores.)
When I refer later to a variable I created using :, do I use : again? For example if I were to execute print(:older_micropost) or print(older_micropost), is there a difference? (See the let statement in the second snippet).
Do they all work the same within a before block as outside one? I am finding that some code will only work inside / outside a before block (eg older_micropost.destroy).
I have looked elsewhere for the answer to this but I can't find a discussion of all three of #, :, and nothing.
Edit: here's a third snippet of code, this time my own. I've commented what works and what doesn't:
describe "deleting a user following" do
let(:userid) { #user.id }
before { print(#user.id.inspect) # this works
#user.destroy } # this works
#user.destroy # this doesn't
print(#user.id.inspect) # this doesn't
subject { other_user }
its(:followed_users) { should_not include(userid) }
end
(Obviously I don't run all 4 lines of commented code together, I run the two inside the before block OR the two outside)
Why do those statements only work inside a before block?
He's mixing the old and new RSpec syntax, which makes it somewhat confusing.
The original incarnation of RSpec used instance variables throughout. So:
before { #user = User.new }
it "should be valid" do
#user.should be_valid
end
RSpec later gained the ability to assign values to variables using let:
let(:user) { User.new }
it "should be valid" do
user.should be_valid
end
let takes a symbol as an argument and defines a method that yields the specified result when the method is referenced. The main advantage of let is that it is lazy-evaluated. This lets you defer the variable setting, which works particularly well when nesting examples.
You can mix and match the two paradigms, as Hartl does, but this can become confusing. It's best to use one style or the other.
Does #user need the # in the first snippet because it's the subject
or..?
It needs to be defined with # or without (using let) and then always referred to the same way. #user and user are not the same thing.
Do I always declare new variables using :? (Actually I'm fairly sure
this isn't the case but I don't understand there whys and wherefores.)
The : is a prefix for a symbol. You would only use it within let.
When I refer later to a variable I created using :, do I use : again?
For example if I were to execute print(:older_micropost) or
print(older_micropost), is there a difference? (See the let statement
in the second snippet).
You use the method name, not the symbol, when referring to a variable. So micropost, not :micropost.
Do they all work the same within a before block as outside one? I am
finding that some code will only work inside / outside a before block
(eg older_micropost.destroy).
Any code that works in an example body should also work in a before block. What will not work is placing code outside an example, eg:
let(:user) { User.new(:name => "Phil") }
before { puts user.name } # "Phil"
it "sets the name" do
puts user.name # "Phil"
end
puts user.name # undefined local variable or method `user'
This is an instance variable, means whenever you need to pass information from controller to views or vice versa we usually usse this #variable
Now : is used for symbols, means they are most of the time similar to strings but they are cheaper than simple strings in terms of memory as it compares the whole string at once.
For more information regarding this, read the article http://www.robertsosinski.com/2009/01/11/the-difference-between-ruby-symbols-and-strings/
I am trying to write an integration test where if a user clicks on a button, it creates a new record in the database (CheckPrice model).
I am running into the error nil is not a symbol when I try to run my test.
require 'spec_helper'
describe 'CheckPrice', type: :request, js: true do
it "should create a new CheckPrice record when user clicks Check Price on topic page" do
city = create :city
hotel = create :hotel
affiliate_link = create :affiliate_link
visit '/hotel-bilboa-hotel'
sleep 2
click_button "Check Prices"
response.should change(CheckPrice.count).by(1)
end
end
When "Check Prices" is clicked, there is an event listener that triggers the new method in the checkprices_controller.
The error seems to occur on the last line response.should change(CheckPrice.count).by(1). It looks like the method does not recognize the model CheckPrice. How do I reference the CheckPrice table?
Thanks.
I don't think you can use the change matcher like this on the response object. Try this:
expect {
click_button "Check Prices"
}.to change{ CheckPrice.count }.by(1)
This makes more semantic sense, too, IMO.
See this cheat sheet for more examples.
Semantic aside, to answer the original question (getting "nil is not a symbol") and help other people who might land here like I did: make sure to use curly brackets {} instead of parentheses ().
So (correct)
response.should change{CheckPrice.count}.by(1)
response.should change(CheckPrice, :count).by(1)
instead of (won't work, a mix of the 2 above)
response.should change(CheckPrice.count).by(1)
Edit:
Same answer with recommended expect syntax
So (correct)
expect{response}.to change{CheckPrice.count}.by(1)
expect{response}.to change(CheckPrice, :count).by(1)
instead of (won't work, a mix of the 2 above)
expect{response}.to change(CheckPrice.count).by(1)
Another way to do this would be:
expect do
click_button "Check Prices"
end.to change(CheckPrice, :count).by(1)
Which indicates that the output of the count method on CheckPrice is what is supposed to be changing. When two parameters are passed to change, one is assumed to be a receiver, the other a symbol to send.
I ran into the same problem, the case is, as the other answers says, that both the methods expect and change, in this case, expect a block as parameter.
So, in rails, you can use either { } or do end syntaxes.