Rspec stubbing method for only specific arguments - ruby

Is there a way to stub method for only specific arguments. Something like this
boss.stub(:fire!).with(employee1).and_return(true)
If any other employee is passed to boss.fire! method, I'll get boss received unexpected message error, but what I would really like is just to override the method for specific argument, and leave it be for all others.
Any ideas how this can be done?

You can add a default stub for the fire! method which will call original implementation:
boss.stub(:fire!).and_call_original
boss.stub(:fire!).with(employee1).and_return(true)
Rspec 3 Syntax (#pk-nb)
allow(boss).to receive(:fire!).and_call_original
allow(boss).to receive(:fire!).with(employee1).and_return(true)

You can try write your own stubbing method, with code like this
fire_method = boss.method(:fire!)
boss.stub!(:fire!) do |employee|
if employee == employee1
true
else
fire_method.call(*args)
end
end

Related

rspec should received x.times a message [duplicate]

I have something like:
value = nil
if some_condition
value =my_object.do_stuff()
end
And in my test, I have the follwing:
MyObject.any_instance.should_receive(:do_stuff)
However, I'd like to just test that the method was called, and have it execute the original code. I'd like to NOT have to do it like:
MyObject.any_instance.should_receive(:do_stuff).and_return(:some_dummy_value)
Is there a way of doing that?
There is and_call_original method:
MyObject.any_instance.should_receive(:do_stuff).and_call_original
See https://github.com/rspec/rspec-mocks#delegating-to-the-original-implementation
I believe, that it's better to create object by FactoryGirl and than to test it. You can read how to make factories and so on.
Example:
FactoryGirl.define do
factory :my_object do
[*here is your attributes*]
end
end
So, after you created a factory, you should to create test where this method used and write this:
my_object = FactoryGirl.create(:my_object)
my_object.should_receive(:do_stuff)
Inside your code you will do that "do_stuff" with your "my_object" when u will run test.

Stub a setter on RSpec instance_double

In an RSpec unit test I have a mock defined like this:
let(:point) { instance_double("Point", :to_coords => [3,2]) }
In the Point class I also have a setter, which is used in the class under test (which is called Robot). I would like to stub that setter to test Robot#move. Here's the wrong code I have so far:
describe "#move" do
it "sets #x and #y one step forward in the direction the robot is facing" do
point.stub(:coords=).and_return([4,2])
robot.move
expect(robot.position).to eq([4,2])
end
end
Here's the error message I get:
Double "Point (instance)" received unexpected message :stub with (:coords=)
Got it! The correct syntax looks like this:
allow(point).to receive(:coords=).and_return([4,2])
The stub method is apparently deprecated.
Another option is to stub the setter method in the definition of the double like so:
let(:point) { double("point", 'coords=' => [4,2]) }
See this github issue for details.

Rspec Ruby Mocking

I would like to achieve 100% coverage on a module. My problem is that there is a variable (called data) within a method which I am trying to inject data in to test my exception handling. Can this be done with mocking? If not how can i fully test my exception handling?
module CSV
module Extractor
class ConversionError < RuntimeError; end
class MalformedCSVError < RuntimeError; end
class GenericParseError < RuntimeError; end
class DemoModeError < RuntimeError; end
def self.open(path)
data = `.\\csv2text.exe #{path} -f xml --xml_output_styles 2>&1`
case data
when /Error: Wrong input filename or path:/
raise MalformedCSVError, "the CSV path with filename '#{path}' is malformed"
when /Error: A valid password is required to open/
raise ConversionError, "Wrong password: '#{path}'"
when /CSVTron CSV2Text: This page is skipped when running in the demo mode./
raise DemoModeError, "CSV2TEXT.exe in demo mode"
when /Error:/
raise GenericParseError, "Generic Error Catch while reading input file"
else
begin
csvObj = CSV::Extractor::Document.new(data)
rescue
csvObj = nil
end
return csvObj
end
end
end
end
Let me know what you think! Thanks
===================== EDIT ========================
I have modified my methods to the design pattern you suggested. This method-"open(path)" is responsible for trapping and raising errors, get_data(path) just returns data, That's it! But unfortunately in the rspec I am getting "exception was expected to be raise but nothing was raised." I thought maybe we have to call the open method from your stub too?
This is what I tried doing but still no error was raised..
it 'should catch wrong path mode' do
obj = double(CSV::Extractor)
obj.stub!(:get_data).and_return("Error: Wrong input filename or path:")
obj.stub!(:open)
expect {obj.open("some fake path")}.to raise_error CSV::Extractor::MalformedCSVError
end
Extract the code that returns the data to a separate method. Then when you test open you can stub out that method to return various strings that will exercise the different branches of the case statement. Roughly like this for the setup:
def self.get_data(path)
`.\\csv2text.exe #{path} -f xml --xml_output_styles 2>&1`
end
def self.open(path)
data = get_data(path)
...
And I assume you know how to stub methods in rspec, but the general idea is like this:
foo = ...
foo.stub(:get_data).and_return("Error: Wrong input filename or path:")
expect { foo.get_data() }.to raise_error MalformedCSVError
Also see the Rspec documentation on testing for exceptions.
Problem with testing your module lies in the way you have designed your code. Think about splitting extractor into two classes (or modules, it's matter of taste -- I'd go with classes as they are a bit easier to test), of which one would read data from external system call, and second would expect this data to be passed as an argument.
This way you can easily mock what you currently have in data variable, as this would be simply passed as an argument (no need to think about implementation details here!).
For easier usage you can later provide some wrapper call, that would create both objects and pass one as argument to another. Please note, that this behavior can also be easily tested.

RSpec -- test if method called its block parameter

I have a method that takes block of code as an argument. The problem is: how to test using RSpec if this method called the block?
The block may be evaluated in any scope the method needs, not necessarily using a yield or block.call. It be passed to another class, or evaluated it in an anonymous class object or somewhere else. For the test to pass it is enough to evaluate the block somewhere as a result of the method call.
Is there a way to test something like this using RSpec?
See also this for more complex case with lets and mocks.
I like using throw instead of raise for this sort of problem, because it can't be rescued be an arbitrary rescue handler. So it might look like this:
my_proc = proc { throw :my_proc_was_called }
expect {
my_proc.call
}.to throw_symbol :my_proc_was_called
I usually do something like
a = 1
b.go { a = 2}
a.should == 2
Thanks to Dave Newton's suggestion in the comment above I did something like this:
it "should run block defining the node" do
message="This message is raised if block is properly evaluated."
expect do
node do
raise message
end
end.to raise_error message
end
In case of error this prints message:
Failure/Error: expect do
expected Exception with "This message is raised if block is properly evaluated." but nothing was raised
Which I find informative enough.
Thanks again for help!

Is there a way to mock/stub "puts" in Rails

I am printing some custom messages in my application using the puts command. However, I do not want these to be appearing in my Test Output. So, I tried a way to stub puts as shown below. But it still outputs my messages. What am I doing wrong ?
stubs(:puts).returns("") #Did not work out
Object.stubs(:puts).returns("") #Did not work out either
puts.stubs.returns "" #Not working as well
Kernel.stubs(:puts).returns "" #No luck
I am using Test::Unit
You probably need to stub it on the actual instance that calls puts. E.g. if you're calling puts in an instance method of a User class, try:
user = User.new
user.stubs(:puts)
user.some_method_that_calls_puts
This similarly applies to when you're trying to test puts in the top-level execution scope:
self.stubs(:puts)
What I would do is define a custom log method (that essentially calls puts for now) which you can mock or silence in test quite easily.
This also gives you the option later to do more with it, like log to a file.
edit: Or if you really want to stub puts, and you are calling it inside an instance method for example, you can just stub puts on the instance of that class.
Using Rails 5 + Mocha: $stdout.stubs(puts: '')
So the comments to the original post point to the answer:
Kernel.send(:define_method, :puts) { |*args| "" }
Instead of silencing all output, I would only silence output from the the particular objects that are putsing during your tests.
class TestClass
def some_method
...
puts "something"
end
end
it "should do something expected" do
TestClass.send(:define_method, :puts) { |*args| "" }
test_class.some_method.should == "abc123"
end

Resources