RSpec Mocking Mailer -- fails when called second time - ruby-on-rails

I'm testing some methods that involve email, and am trying to use a mock mailer object. I'm obviously going about it the wrong way, because the test works the first time but the very same test fails subsequently. I'd appreciate it if anyone can explain what is going on here. Thanks.
describe SentMessage do
before(:each) do
Notifier ||= mock('Notifier', :send_generic => true)
end
it "Sends an email" do
Notifier.should_receive(:send_generic).with(['a#contact.com'], 'test')
Notifier.send_generic(['a#contact.com'], 'test').should_not be_nil
end
it "Sends an email" do
Notifier.should_receive(:send_generic).with(['a#contact.com'], 'test')
Notifier.send_generic(['a#contact.com'], 'test').should_not be_nil
end
end
Result:
Failures:
1) SentMessage Sends an email
Failure/Error: Notifier.send_generic(['a#contact.com'], 'test').should_not be_nil
expected: not nil
got: nil
# ./spec/models/test.rb:14:in `block (2 levels) in <top (required)>'

Rspec inserts setup/teardown stuff for mocks and expectations. This does stuff like verify that should_receive expectations have been met and clear up mocks set in objects that endure beyond a single spec. For example if you had stubbed User.find in one spec, you wouldn't expect that stub to exist in another spec.
So at the end of the first spec, rspec is removing the stub setup in the before each. Because you're doing ||=, Notifier isn't recreated and neither is the stub. This in turn means that when you call should_receive in the second spec you're setting up a new stub. Since this new stub has no specified return value, nil is returned.

Related

how to check if a method is called or not in rspec

as i try to check internally my method was calling or not in rspec but it got the following errors
context "#Meeting_schedule" do
let(:meeting_schedule) { FactoryGirl.create(:meeting_schedule,:time=>"morning",:schedule_name=>"planned_meet", :schedule_info=>[{ "from"=>"00:00", "to"=>"00:01"}]) }
it "if the same schedule was created again dont save it again" do
schedule.save
params = {:time=>"morning",:schedule_name=>"planned_meet", :schedule_info=>[{ "from"=>"00:00", "to"=>"00:01"}]}
meeting_schedule.create_or_update_meeting_schedule(params)
expect(meeting_schedule).to receive(:updating_the_user)
end
end
i got the following error
Failure/Error: expect(meeting_schedule.create_or_update_meeting_schedule(params)).to receive(:updating_the_user)
(#<Meeting_schedule:0x0055dbaf0da710>).updating_the_user(*(any args))
expected: 1 time with any arguments
received: 0 times with any arguments
# ./spec/models/meeting_schedule_spec.rb:122:in `block (4 levels)
so what was wrong in my code?
my method
def create_or_update_meeting_schedule(params)
self.attributes = params
if self.changed and self.save
updating_the_user
end
self
end
can anyone help me out
Mocks must always be setup before the method under test is called as there is no reliable way to test if a normal method was called in Ruby. There are two ways of doing this in RSpec.
The first is using expect(...).to receive(...) which must be done before the method is called - this detaches the method and replaces it with a mock that wraps the original method.
The test will fail if the method is not called in the example.
The second is by using spies. You can either replace an entire object with a spy:
RSpec.describe "have_received" do
it "passes when the message has been received" do
invitation = spy('invitation')
invitation.deliver
expect(invitation).to have_received(:deliver)
end
end
This "spy object" will keep track of any method you call on it.
You can also spy on a single method:
class Invitation
def self.deliver; end
end
RSpec.describe "have_received" do
it "passes when the expectation is met" do
allow(Invitation).to receive(:deliver)
Invitation.deliver
expect(Invitation).to have_received(:deliver)
end
end
Spies are very useful in the case when you want to mock the method in the test setup - for example in the before block.
class Invitation
def self.deliver; end
end
RSpec.describe "have_received" do
before do
allow(Invitation).to receive(:deliver)
end
it "passes when the expectation is met" do
Invitation.deliver
expect(Invitation).to have_received(:deliver)
end
end

RSpec3 and message expectations

I'm using rspec 3.0.3 and ruby 2.1.2 and just can't figure out what's going wrong.
Sorry for not good code implementation (I mean that class variables), but it was the easier way to show whats going wrong.
I have 2 classes. First calling new_method of the Test class should call AnotherTest.call_method that should change ##c_var class variable.
require "rspec"
class Test
def self.new_method
AnotherTest.call_method
end
end
class AnotherTest
##c_var = "hola"
def self.call_method
##c_var = "holla from another test"
end
def self.c_var
##c_var
end
end
And I'm writing specs for it:
describe Test do
it "should call method from an other class" do
Test.new_method
expect(AnotherTest.c_var).to be_eql("holla from another test")
end
end
And this specs is working OK. But then I'm trying to use "expect to receive call" something goes wrong
describe Test do
it "should call method from an other class" do
expect(AnotherTest).to receive(:call_method).and_return("holla from another test")
Test.new_method
expect(AnotherTest.c_var).to be_eql("holla from another test")
end
end
Failures:
1) Test should call method from an other class
Failure/Error: expect(AnotherTest.c_var).to be_eql("holla from another test")
expected `"hola".eql?("holla from another test")` to return true, got false
# ./test.rb:26:in `block (2 levels) in <top (required)>'
Seems like RSpec is making this check in something like a migration and doing a rollback after it.
It's such a strange sample, I know, but I've noticed this bug only then method of one instance of a class is calling method from the other instance and that method is trying to change something.
By using expect(...).to receive(...), the original method is not being called. Rather, when it is called, it just returns whatever you passed into and_return(...) without actually executing your method.
What you probably want is and_call_original. This way, you can ensure the method is called, and still allow it to execute:
expect(AnotherTest).to receive(:call_method).and_call_original
Source: https://www.relishapp.com/rspec/rspec-mocks/v/3-0/docs/configuring-responses/calling-the-original-implementation

Configure rspec to show 'E' error when running an erroneous test

TL;DR: How can I stop rspec aborting all specs when it encounters an error?
When running rails' built in testing suite, I get output similar to the following:
...F..F.EE...E
Finished in 0.64396 seconds
14 examples, 2 failures, 3 errors
The . represents a passing test, the F a failing test and the E an erroneous test. A failing test means the code of your rails application is failing, and an erroneous test means the code of your actual test is failing. Erroneous tests are never good.
What I do like about this though, is that when the program encounters an E, it keeps on going through all the other tests.
I'm using rpsec, and while I do like it, I sort of hate how, when it encounters an erroneous spec, it completely gives up the ghost, and quits all specs, like this:
12:39:37 - INFO - Running: spec
/usr/local/rvm/rubies/ruby-1.9.3-p392/lib/ruby/gems/1.9.1/gems/activerecord-4.0.1/lib/active_record/validations.rb:57:in `save!': Validation failed: Email has already been taken (ActiveRecord::RecordInvalid)
from /usr/local/rvm/rubies/ruby-1.9.3-p392/lib/ruby/gems/1.9.1/gems/activerecord-4.0.1/lib/active_record/attribute_methods/dirty.rb:41:in `save!'
from /usr/local/rvm/rubies/ruby-1.9.3-p392/lib/ruby/gems/1.9.1/gems/activerecord-4.0.1/lib/active_record/transactions.rb:275:in `block in save!'
from /usr/local/rvm/rubies/ruby-1.9.3-p392/lib/ruby/gems/1.9.1/gems/activerecord-4.0.1/lib/active_record/transactions.rb:326:in `block in with_transaction_returning_status'
While I do like the error reporting, I really want it to carry on running all specs when it encounters an erroneous spec. Rather than the above output, I really want something along the lines of the following:
12:40:01 - INFO - Running: spec
...F..F..E
Failures:
1) User A new user can be created email should == "awesomedog#hotmail.co.uk"
Failure/Error: its(:email) { should == "awesomedog#hotmail.co.uk" }
expected: "awesomedog#hotmail.co.uk"
got: "awesomedozg#hotmail.co.uk" (using ==)
# ./spec/model/user_spec.rb:11:in `block (3 levels) in <top (required)>'
2) User A new user can be created should not be valid
Failure/Error: it { should_not be_valid }
expected valid? to return false, got true
# ./spec/model/user_spec.rb:20:in `block (3 levels) in <top (required)>'
Errors:
1) `save!': Validation failed: Email has already been taken (ActiveRecord::RecordInvalid)
Finished in 0.64396 seconds
10 examples, 2 failures, 1 error
Failed examples:
rspec ./spec/model/user_spec.rb:11 # User A new user can be created email should == "awesomedog#hotmail.co.uk"
rspec ./spec/model/user_spec.rb:20 # User A new user can be created should not be valid
I'm using factory-girl and factories in my specs, and data-base cleaner to clean my database between each spec. I'm using rspec-guard to run all specs on the event of any project file (apart from those in tmp,log or db) being saved. Because rpsec keeps wimping out if it hits an error, I'm getting this error:
How can I clean my database between erroneous rspec specs?
Basically, database-cleaner is configured to clean my database when a spec begins and when a spec finishes. Because rspec quits in the middle of a spec when it hits an error, database-cleaner doesn't detect that the spec has finished, and so never cleans my database. This means I have to manually empty it with my database shell.
I'd also prefer to see the state of all my other specs, even if one is erroneous! Rspec is really silly in this regard methinks!
Here are my factories and my spec:
spec/model/user_spec.rb:
require 'spec_helper'
describe User do
context "Valid user" do
user = FactoryGirl.create(:user_with_all_valid)
subject { user }
its(:first_name) { should == "Jimmy" }
its(:last_name) { should == "Thehat" }
its(:profile_name) { should == "Jimbohatboy893" }
its(:email) { should == "awesomedog#hotmail.co.uk" }
its(:password) { should == "thisisasupersecretpassword12234234" }
its(:password_confirmation) { should == "thisisasupersecretpassword12234234" }
end
end
spec/factories.rb:
FactoryGirl.define do
factory :user_with_all_valid, class: User do
first_name "Jimmy"
last_name "Thehat"
profile_name "Jimbohatboy893"
email "awesomedog#hotmail.co.uk"
password "thisisasupersecretpassword12234234"
password_confirmation "thisisasupersecretpassword12234234"
end
end
Your issue is this line:
context "Valid user" do
user = FactoryGirl.create(:user_with_all_valid) # Right here!
...
Test setup should always be done in a before block; variable definition is done in a let block. Doing it in your test definition will cause rspec to fail as described. In general, you should only ever have rspec directives (before, after, let, subject, it, its, etc) in a context block. If you're running test setup, application code, whatever, you're going to open yourself up to this kind of problem. What you want is:
context "Valid user" do
let(:user) { FactoryGirl.create(:user_with_all_valid) }
or:
context "Valid user" do
before { #user = FactoryGirl.create(:user_with_all_valid) }
The suite will then run the whole thing as expected, and report failures for the tests that fail to do their defined setup (the user definition) properly, rather than bombing out of the whole suite.
You need to have a unique email each time due to the validation on the model.
btw the other 7 or 8 are passing (dots).
Try adding:
new_user_time = Time.new
to save the current time.
Then pass that in to the create user factory.
Then use it to check that the users email is correct.
Use sequences as detailed at: https://github.com/thoughtbot/factory_girl/blob/master/GETTING_STARTED.md#sequences
Unique values in a specific format (for example, e-mail addresses) can be generated using sequences. Sequences are defined by calling sequence in a definition block, and values in a sequence are generated by calling FactoryGirl.generate:
# Defines a new sequence
FactoryGirl.define do
sequence :email do |n|
"person#{n}#example.com"
end
end
FactoryGirl.generate :email
# => "person1#example.com"
FactoryGirl.generate :email
# => "person2#example.com"
...
The just have your spec check that part of the email address is correct, e.g. the #something.com or use a regular expression to compare, e.g. match(/person.*#something\.com/)

separate helper function to log in before every rspec request test is run

I've been struggling with creating a login function that should be executed before any rspec test is run.
What I have right now is:
def login
post "/session", { "session[username]" => "bjones" }
end
in my spec_helper.rb file
Then, I have the following in one of my spec.rb files in the requests directory.
require 'spec_helper'
describe "Sessions" do
describe "GET /dashboards" do
login
it "displays the dashboard" do
get dashboard_path
puts response.body
end
end
end
However, when I try running the test, I get:
undefined method `post' for #<Class:0x4f08828> (NoMethodError)
I'm pretty new to rails and completely new to testing and rspec so maybe there's something fundamental I'm missing here. Basically, all I want to do is set that session variable so that when the test is run I will be logged in. Perhaps a different approach would be better? Or maybe I need to put that login function in a different place?
I came across this answer which was sort of useful but it's not for rspec so I'm having trouble understanding where such a helper function would go.
Try
let(:login) {
post "/session", { "username" => "bjones" }.to_json
}
This might have to be revised to use .to_json or not, depending on what content type the controller accepts.

Rspec integration test (involving check box) won't pass

I'm working on a goal application, where a user can check a box next to a goal to mark it as complete. I wrote the test for this functionality, but something isn't quite right, because it keeps failing. I eventually got fed up and just wrote the code, which works, but the test still keeps failing. I'm still learning Rspec, so any advice you could give me would be helpful.
The Test
# #user has already been signed in
describe "completing a goal" do
before(:each) do
#goal = Factory(:goal, :user => #user)
end
it "should set 'completed' attribute to true" do
visit edit_goal_path(#goal)
check('goal_completed')
click_button('goal_submit')
#goal.completed.should be_true
end
end
The Result
Failures:
1) Goals completing a goal should set 'completed' attribute to true
Failure/Error: #goal.completed.should be_true
expected nil to be true
# ./spec/requests/goals_spec.rb:81:in `block (3 levels) in <top (required)>'
If you want this to be an integration test, it would be better for your assertion to be about the contents of the page. Something like:
assert_text "Goal met on 1/1/2011"
If you want to stick with model assertions, I'm pretty sure you just need to reload:
#goal.reload.completed.should be_true

Resources