Rspec allow and expect the same method with different arguments - ruby-on-rails

I want to test my method which runs method Temp::Service.run two times inside it:
module Temp
class Service
def self.do_job
# first call step 1
run("step1", {"arg1"=> "v1", "arg2"=>"v2"})
# second call step 2
run("step2", {"arg3"=> "v3"})
end
def self.run(name, p)
# do smth
return true
end
end
end
I want to test arguments provided to second call of method :run with first argument 'step2'
while I want to ignore the first call of the same method :run but with first argument 'step1'.
I have the RSpec test
RSpec.describe "My spec", :type => :request do
describe 'method' do
it 'should call' do
# skip this
allow(Temp::Service).to receive(:run).with('step1', anything).and_return(true)
# check this
expect(Temp::Service).to receive(:run) do |name, p|
expect(name).to eq 'step2'
# check p
expect(p['arg3']).not_to be_nil
end
# do the job
Temp::Service.do_job
end
end
end
but I got error
expected: "step2"
got: "step1"
(compared using ==)
How to correctly use allow and expect for the same method ?

Seems like you are missing the .with('step2', anything)
it 'should call' do
allow(Temp::Service).to receive(:run).with('step1', anything).and_return(true)
# Append `.with('step2', anything)` here
expect(Temp::Service).to receive(:run).with('step2', anything) do |name, p|
expect(name).to eq 'step2' # you might not need this anymore as it is always gonna be 'step2'
expect(p['arg3']).not_to be_nil
end
Temp::Service.do_job
end

Related

Is there a way to run a nested before filter first in RSpec?

describe 'Feature' do
before do
setup
end
describe 'Success' do
before do
setup_for_success
end
specify 'It works' do
...
end
end
end
RSpec will always run the setup before setup_for_success. It there a way to run setup_for_success first?
You can do this by scoping a before(:all) to run before a before(:each) try this:
describe 'Feature' do
before(:each) do
puts "second"
end
describe 'Success' do
before(:all) do
puts "first"
end
specify 'It works' do
...
end
end
end
# =>
10:29:54 - INFO - Running: spec
Run options: include {:focus=>true}
first
second
.
Finished in 0.25793 seconds (files took 2.52 seconds to load)
1 example, 0 failures
EDIT:
In Rspec 2, the actions run in this order:
before suite
before all
before each
after each
after all
after suite
Here's a link to the docs showing the order that the methods are called in: https://www.relishapp.com/rspec/rspec-core/v/2-2/docs/hooks/before-and-after-hooks#before/after-blocks-are-run-in-order
Apparently in Rspec 3.5, the before block calls have a different naming that also works. They run in this order:
before :suite
before :context
before :example
after :example
after :context
after :suite
describe 'Feature' do
before(:example) do
puts "second"
end
describe 'Success' do
before(:context) do
puts "first"
end
specify 'It works' do
...
end
end
end
10:59:45 - INFO - Running: spec
Run options: include {:focus=>true}
first
second
.
Finished in 0.06367 seconds (files took 2.57 seconds to load)
1 example, 0 failures
Here's the newer docs:
http://www.relishapp.com/rspec/rspec-core/v/3-5/docs/hooks/before-and-after-hooks
before filters are appended in the order they're specified. Since RSpec 2.10.0, you can prepend them instead by making them prepend_before filters.
Likewise, after filters are prepended by default, but you can append_after them instead.
Your code would end up as follows (compacted for brevity):
describe 'Feature' do
before { setup }
describe 'Success' do
prepend_before { setup_for_success }
it 'works' { ... }
end
end
It seems a little weird you put in a nested context what you need in the outer one. I suspect that you don't need that setup in all of the nested contexts. If that's the case you need to filtering your hooks.
RSpec.describe 'Feature' do
before :each, success: true do
setup_for_success
end
before :each do
setup
end
describe 'Success', success: true do
specify 'It works' do
...
end
end
describe 'Fail' do
specify 'Won´t work' do
...
end
end
end
You can do this without nesting:
RSpec.describe 'Feature' do
before :each, success: true do
setup_for_success
end
before :each do
setup
end
specify 'It works', success: true do
...
end
specify 'Won´t work' do
...
end
end
Here is the link to the docs:
https://relishapp.com/rspec/rspec-core/docs/hooks/filters

How to test that a method is called using rspec?

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

Rails tutorial, chapter 6. error test: User return value of authenticate method with invalid password should be false [duplicate]

I am running this portion of a test:
describe Dictionary do
before do
#d = Dictionary.new
end
it 'can check whether a given keyword exists' do
#d.include?('fish').should be_false
end
With this code:
class Dictionary
def initialize
#hash = {}
end
def add(new_entry)
new_entry.class == String ? #hash[new_entry] = nil : new_entry.each { |noun, definition| #hash[noun] = definition}
end
def entries
#hash
end
def keywords
#hash.keys
end
def include?(word)
if #hash.has_key?(word)
true
else
false
end
end
end
I don't know what I'm doing wrong, but my tests keep failing and saying this:
> 1) Dictionary can check whether a given keyword exists
> Failure/Error: #d.include?('fish').should be_false
> expected false to respond to `false?`
I am confused at the error since it seems to be giving the correct answer. I would really appreciate if someone could take a few minutes to tell me what's wrong with my code.
Thank you tons.
If you browse the RSpec Expectations 2.99 and RSpec Expectations 2.14 and search the section - Truthiness and existentialism, you will find
expect(actual).to be_true # passes if actual is truthy (not nil or false)
expect(actual).to be_false # passes if actual is falsy (nil or false)
# ...............
# ...
But of you browse RSpec Expectations 3.0 , the above method names got changed to -
expect(actual).to be_truthy # passes if actual is truthy (not nil or false)
expect(actual).to be true # passes if actual == true
expect(actual).to be_falsey # passes if actual is falsy (nil or false)
# ...........
#......
It seems you are in 3.0, and using the method which were exist prior to this version. Thus you were getting the error.
I put the code in my test.rb file as below :-
class Dictionary
def initialize
#hash = {}
end
def add(new_entry)
new_entry.class == String ? #hash[new_entry] = nil : new_entry.each { |noun, definition| #hash[noun] = definition}
end
def entries
#hash
end
def keywords
#hash.keys
end
def include?(word)
if #hash.has_key?(word)
true
else
false
end
end
end
And my spec/test_spec.rb file is -
require_relative "../test.rb"
describe Dictionary do
before do
#d = Dictionary.new
end
it 'can check whether a given keyword exists' do
#d.include?('fish').should be_false
end
end
Now I am running the code from my console, and it works :
arup#linux-wzza:~/Ruby> rspec -v
2.14.8
arup#linux-wzza:~/Ruby> rspec spec
.
Finished in 0.00169 seconds
1 example, 0 failures
Now I am changing the code in my spec/test_spec.rb file :-
require_relative "../test.rb"
describe Dictionary do
before do
#d = Dictionary.new
end
it 'can check whether a given keyword exists' do
#d.include?('fish').should be_falsey
end
end
and again run the test :-
arup#linux-wzza:~/Ruby> rspec -v
2.14.8
arup#linux-wzza:~/Ruby> rspec spec
F
Failures:
1) Dictionary can check whether a given keyword exists
Failure/Error: #d.include?('fish').should be_falsey
NoMethodError:
undefined method `falsey?' for false:FalseClass
# ./spec/test_spec.rb:9:in `block (2 levels) in <top (required)>'
Finished in 0.00179 seconds
1 example, 1 failure
Failed examples:
rspec ./spec/test_spec.rb:8 # Dictionary can check whether a given keyword exists
arup#linux-wzza:~/Ruby>
Now, they also mentioned in the 3.0.0.beta1 / 2013-11-07 changelog
Rename be_true and be_false to be_truthy and be_falsey. (Sam Phippen)

RSpec - "POST" action gets called only once

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/

RSpec - How to create helper method available to tests that will automatically embed "it" tests

I am new to ruby/rails/rspec etc.
Using rspec 2.13.1, I want to create a module with a method that can be called from my tests resulting to subsequent calls of the "it" method of the RSpec::Core::ExampleGroup.
My module:
require 'spec_helper'
module TestHelper
def invalid_without(symbols)
symbols = symbols.is_a?(Array) ? symbols : [symbols]
symbols.each do |symbol|
it "should not be valid without #{symbol.to_s.humanize}" do
# Gonna nullify the subject's 'symbol' attribute here
# and expect to have error on it
end
end
end
end
The code above was added to:
spec/support/test_helper.rb
and in my spec_helper.rb, in the RSpec.configure block, I added the following:
config.include TestHelper
Now, in a test, I do the following:
describe Foo
context "when invalid" do
invalid_without [:name, :surname]
end
end
Running this, I get:
undefined method `invalid_without' for #<Class:0x007fdaf1821030> (NoMethodError)
Any help appreciated..
Use shared example group.
shared_examples_for "a valid array" do |symbols|
symbols = symbols.is_a?(Array) ? symbols : [symbols]
symbols.each do |symbol|
it "should not be valid without #{symbol.to_s.humanize}" do
# Gonna nullify the subject's 'symbol' attribute here
# and expect to have error on it
end
end
end
describe Foo do
it_should_behave_like "a valid array", [:name, :surname]
end

Resources