I have a very short function to test:
def my_fn
if some_condition
super(#my_attr)
end
end
And I want my spec to do verify that super gets called, but I can't do the following because :super appears not to be a message that ever gets sent:
expect(subject).to receive(:super)
Related
In my public method #recalculate, calling the private method1. This method throw exception 'StandardError'. I want to test this scenario, however getting an error.
Note: I don't want to handle an exception.
def recalculate
method_1
end
private
def method_1
## do some calculation
raise StandardError.new("Test")
end
Rspec Test case:
it "Test" do
expect { #product.recalculate.recalculate }.to raise_error(StandardError)
#product.recalculate
end
1) Product.Test
Failure/Error: #product.recalculate
StandardError:
Test
(required)>'
Finished in 1.39 seconds
1 example, 1 failure
According to your example, the second line #product.recalculate raises an actual exception hence the error. Assuming recalculate method is defined in #product object, this should be enough to test it.
it "Test" do
expect { #product.recalculate }.to raise_error(StandardError)
end
I am trying to test that a part of my code is running a DelayedJob.
Here's code:
def start_restream
...
puts 'Here'
Delayed::Job.enqueue(Restream::StartAllJob.new(channel.id))
puts 'After'
...
end
#app/jobs/restream/start_all_job.rb
class Restream::StartAllJob < Struct.new(:channel_id)
def perform
puts "Inside"
...
end
end
In my spec_helper.rb I have Delayed::Worker.delay_jobs = false.
The spec:
it 'runs Delayed::Job::Restream::StartAll' do
post :start_restream, :id => channel.id
expect(Restream::StartAllJob).to receive(:new)
end
It prints out
Here
Inside
After
when running, so I know that it is called. But test fails:
Failure/Error: expect(Restream::StartAllJob).to receive(:new)
(Restream::StartAllJob (class)).new(*(any args))
expected: 1 time with any arguments
received: 0 times with any arguments
Or, if I use expect_any_instance_of(Restream::StartAllJob).to receive(:perform) it says
Failure/Error: example.run
Exactly one instance should have received the following message(s) but didn't: perform
What am I doing wrong and how can I test this?
It's just the mistake I made in the order:
expect(Restream::StartAllJob).to receive(:new) should be written before post :start_restream, :id => channel.id
In a model spec, I want to test that certain methods are being called correctly.
#models/object.rb
class Object < ActiveRecord::Base
after_validation :do_this
after_save :enqueue_that
def do_this
# does some stuff, the results of which I don't want to test
end
def enqueue_that
MyWorker.perform_later id
end
end
#spec/models/object.rb
describe Object
describe '#do_this' do
it 'is called on save with passing validations' do
object.save
expect(object).to receive(:do_this)
end
end
describe '#enqueue_that' do
it 'is called after save' do
object.save
expect(MyWorker).to receive(:perform_later).once
end
end
end
The tests are failing with the following
Failure/Error: expect(object).to receive(:do_this).once
(#<Object:0x007fd2101c7160>).do_this(*(any args))
expected: 1 time with any arguments
received: 0 times with any arguments
Failure/Error: expect(MyWorker).to receive(:perform_later).once
(MyWorker (class)).perform_later(*(any args))
expected: 1 time with any arguments
received: 0 times with any arguments
Confusingly, these methods appear to be behaving correctly in the dev environment.
Am I using expect().to receive correctly? Or have my tests uncovered a genuine bug?
You just have things in the wrong order...
it 'is called on save with passing validations' do
expect(object).to receive(:do_this)
object.save
end
I have a method in my controller, unreserve, that calls the find_ticket method and, if the ticket is found, calls unreserve on the ticket.
def unreserve
if find_ticket
#ticket.unreserve current_or_guest_user
render json: #ticket.show.tickets_as_json(current_or_guest_user)
else
render json: nil
end
end
private
def find_ticket
#ticket = Ticket.find params[:id]
rescue ActiveRecord::RecordNotFound
false
else
true
end
Since I have already tested unreserve, I just want to make sure that method is called. So when I try running this spec:
describe 'unreserving a ticket' do
before :each do
sign_in customer
put :reserve, id: ticket_1.id
put :unreserve, id: ticket_1.id
end
it 'calls unreserve on the ticket with the current user' do
expect(controller.instance_variable_get :#ticket).to receive(:unreserve)
end
end
Which I would expect to pass, I even threw a puts statement inside the unreserve method to make sure that method was being called, and it outputted to the console when I was running the test.
The output I get from the test is:
1) TicketsController unreserving a ticket calls unreserve on the ticket with the current user
Failure/Error: expect(controller.instance_variable_get :#ticket).to receive(:unreserve)
(#<Ticket:0x007f90dd6c9600>).unreserve(*(any args))
expected: 1 time with any arguments
received: 0 times with any arguments
EDIT:
I tried calling the expect statement before the actions, but still got the same error. Here is the new test:
it 'calls unreserve on the ticket with the current user' do
expect(controller.instance_variable_get :#ticket).to receive(:unreserve)
sign_in customer
put :reserve, id: ticket_1.id
put :unreserve, id: ticket_1.id
end
Unless I'm remembering incorrectly you need to make your expectations before you make your request. Meaning you can't use a before block (well not entirely). Move your put :unreserve down after your expect call like so:
describe 'unreserving a ticket' do
before :each do
sign_in customer
put :reserve, id: ticket_1.id
end
it 'calls unreserve on the ticket with the current user' do
expect(controller.instance_variable_get :#ticket).to receive(:unreserve)
put :unreserve, id: ticket_1.id
end
end
I have a problem with calling 'POST' method only once in my test suits.
let(:foo) {post :foo_controller arguments}
it 'FIRST: should validate post response first field' do
expect(foo[:first_field]).to match('something')
end
it 'SECOND: should validate post response second field' do
expect(foo[:second_field]).to match('something else')
end
Now foo's "POST" action is called twice.
I would like to make it that FIRST requests 'POST' and gets a request value, but SECOND only gets a value, which is persisted, without calling this 'POST'.
Is there an elegant way to solve this problem?
You could use a before(:all) block, not sure what post actually returns though.
before(:all) do
#my_response = post :foo_controller arguments
end
Hope that helps!
I us a small helper to fix this issue for me.
This is the helper module:
module ControllerSpecHelpers
# example:
#
# describe 'my request' do
# examine_response {get '/foo/bar'}
# it {should be_ok}
# end
#
#
def examine_response &block
before(:all) do
self.instance_exec &block
end
subject {last_response}
end
end
I configure Rspec to use it in my spec helper:
RSpec.configure do |conf|
# snip ...
conf.extend ControllerSpecHelpers
end
Then when I need to execute the call only once, and test multiple properties, I use it like this:
describe "when signing up" do
examine_response do
post "/signup", {email: 'signup#test.com', password: 'password'}
end
it {should be_ok}
it "body says welcome" do
expect(subject.body).to include 'welcome'
end
end
Here's more detail on how extending Rspec works:
http://timnew.github.io/blog/2012/08/05/extend-rspec-dsl/