Rspec test - keep executing a method until the error message is no longer raised - ruby

I'm new to Rspec, so sorry if this is a bad question. In one of the test tests I'm running, I have code which uses a random number generator to determine whether or not a method should be executed. If it cannot be executed, the method raises an error message.
So I need to write a test which continually runs the method on a small array of class objects until it no longer receives the error message. So in effect each class object will eventually successfully execute that method after a few tries.
The array has 6 items. I'm hoping that I need to loop through each one and then use a while loop which then tests whether the error message has been executed, but I haven't got a clue how. Any help gratefully appreciated.
I have something like this at the moment...
def create_planes
6.times do
plane=Plane.new
planes<<plane
end
end
it 'should land each plane' do
create_planes
i = 0
while i<planes.count
begin
airport.plane_land(planes[i])
i++
rescue
next
end
end
expect(airport.plane_count).to eq(6)
end

Generally with RSpec, you'll set up your inputs and test your outputs. You wouldn't execute until an error occurs, you'd execute a known number of lands and then check that the airport's plane changed to what you expect.
it "should maintain a list of landed planes" do
expect {
3.times { airport.plane_land Plane.new }
}.to change { airport.plane_count }.from(0).to(3)
end

Related

RSpec: Expecting a method to be called causes that method to not actually be called

I have some code that could be represented in very simple terms as:
def method_a(key)
hash = method b(key)
hash.delete(key)
end
def method_b(key)
return { key => 1 }
end
and then an rspec test like
it 'calls method_b'
expect(someClass).to receive(:method_b).with(key)
method_a(key)
end
However I then get an error in the second line of method_a because it's trying to call delete on a nil object. When I debug, I can see that the logic inside method_b is never actually being invoked. It's not failing somewhere in method_b, it's literally not calling it at all. If I get rid of the expect statement in the test, this error goes away. It seems like the expect statement is causing it to just skip over the actual call to method_b, leaving me with a nil value instead of the hash I'm expecting.
Is there a way I can stop it from skipping over method_b, or at least terminate execution once the expect statement is successful, so I don't run into the error on the next line?
When you set a message expectation, it overrides the original code, unless you explicitly tell RSpec not to:
expect(someClass).to receive(:method_b).with(key).and_call_original

How to stub a ruby method and conditionally raise an exception based upon state of object

In short, I want to raise an exception via a stubbed method, but only if the object that has the stubbed method has a particular state.
Mail::Message.any_instance.stub(:deliver) do
if to == "notarealemailaddress!##!##"
raise Exception, "SMTP Error"
else
return true
end
end
This doesn't work, because the context inside the stub block is: RSpec::Core::ExampleGroup::Nested_1::Nested_2::Nested_2.
How do I get access to the stubbed object?
using ruby 2, rspec 2.
The actual scenario is I have an app that sounds out thousands of emails in batches and I have code that catches SMTP exceptions, logs the batch, and proceeds. So I want to test sending several batches, where one of the batches in the middle throws an exception.
It looks like this is solved in the latest(currently alpha) version of Rspec v3:
https://github.com/rspec/rspec-mocks/commit/ebd1cdae3eed620bd9d9ab08282581ebc2248535#diff-060466b2a68739ac2a2798a9b2e78643
it "passes the instance as the first arg of the implementation block" do
instance = klass.new
expect { |b|
klass.any_instance.should_receive(:bees).with(:sup, &b)
instance.bees(:sup)
}.to yield_with_args(instance, :sup)
end
I believe you specify the arguments using the with method, so in your case it would be something along the lines of:
Mail::Message.any_instance.stub(:deliver).with(to: "notarealemailaddress!##!##") do
raise Exception, "SMTP Error"
end
There's full documentation here:
https://www.relishapp.com/rspec/rspec-mocks/v/2-3/docs/method-stubs
Ok, here's how you can get this behavior fairly easily without upgrading:
class Rspec::Mocks::MessageExpectation
# pulling in behavior from rspec v3 that I really really really need, ok?
# when upgrading to v3, delete me!
def invoke_with_orig_object(parent_stub, *args, &block)
raise "Delete me. I was just stubbed to pull in behavior from RSpec v3 before it was production ready to fix a bug! But now I see you are using Rspec v3. See this commit: https://github.com/rspec/rspec-mocks/commit/ebd1cdae3eed620bd9d9ab08282581ebc2248535#diff-060466b2a68739ac2a2798a9b2e78643" if RSpec::Version::STRING > "2.99.0.pre"
args.unshift(#method_double.object)
invoke_without_orig_object(parent_stub, *args, &block)
end
alias_method_chain :invoke, :orig_object
end
Drop that at the bottom of your spec file. You'll notice I even add a check to raise an error once RSpec is upgraded. boom!

Data driven testing with ruby testunit

I have a very basic problem for which I am not able to find any solution.
So I am using Watir Webdriver with testunit to test my web application.
I have a test method which I would want to run against multiple set of test-data.
While I can surely use old loop tricks to run it but that would show as only 1 test ran which is not what I want.
I know in testng we have #dataprovider, I am looking for something similar in testunit.
Any help!!
Here is what I have so far:
[1,2].each do |p|
define_method :"test_that_#{p}_is_logged_in" do
# code to log in
end
end
This works fine. But my problem is how and where would I create data against which I can loop in. I am reading my data from excel say I have a list of hash which I get from excel something like
[{:name =>abc,:password => test},{:name =>def,:password => test}]
Current Code status:
class RubyTest < Test::Unit::TestCase
def setup
#excel_array = util.get_excel_map //This gives me an array of hash from excel
end
#excel_array.each do |p|
define_method :"test_that_#{p}_is_logged_in" do
//Code to check login
end
end
I am struggling to run the loop. I get an error saying "undefined method `each' for nil:NilClass (NoMethodError)" on class declaration line
You are wanting to do something like this:
require 'minitest/autorun'
describe 'Login' do
5.times do |number|
it "must allow user#{number} to login" do
assert true # replace this assert with your real code and validation
end
end
end
Of course, I am mixing spec and test/unit assert wording here, but in any case, where the assert is, you would place the assertion/expectation.
As this code stands, it will pass 5 times, and if you were to report in story form, it would be change by the user number for the appropriate test.
Where to get the data from, that is the part of the code that is missing, where you have yet to try and get errors.

Alternative to rspec double that does not fail test even if allow receive is not specified for a function

Many times one outcome may have two different consequences that need to be tested with a test double. For example if a network connection is successful I'd like to log a message, and also pass the resource to another object that will store it internally. On the other hand it feels unclean to put these two in one test. For example this code fails:
describe SomeClass do
let(:logger) { double('Logger') }
let(:registry) { double('Registry') }
let(:cut) { SomeClass.new }
let(:player) { Player.new }
describe "#connect" do
context "connection is successful" do
it "should log info" do
logger.should_receive(:info).with('Player connected successfully')
cut.connect player
end
it "should register player" do
registry.should_receive(:register).with(player)
cut.connect player
end
end
end
end
I could specify in each test that the function in the other one might get called, but that looks like unnecessary duplication. In that case I'd rather make this one test.
I also don't like that it's never explicit in the test that a method should NOT be called.
Does anyone know about an alternative that has an explicit 'should_not_receive' message instead of automatically rejecting calls that are not explicitly specified?
RSpec supports should_not_receive, which is equivalent to should_receive(...).exactly(0).times as discussed in this message from the original author of RSpec.

Is there a way to get a stack trace from rspec when a method is unexpectedly called more times than specified?

I setup a mock object and told it to expect a check for nil and to return false:
status = double('status')
status.should_receive(:nil?).and_return(false)
I only expect the call to nil? to occur once, but I got an error in my rspec test, saying that status received nil? twice.
Is there a way to get rspec to show where/how each call occurred?
adding the '--backtrace' option did not work.
Try something like this:
status.should_receive(:nil?).twice { puts caller; false }
This tells rspec to allow two invocations and call the associated block each time. Thecaller method generates a full backtrace which you should be able to analyze onstdout. We also returnfalse to stay on the code-path we're testing.
If the two backtraces are hard to distinguish and you're only interested in the second (unexpected) invocation, then set up two successive expectations:
status.should_receive(:nil?).and_return(false)
status.should_receive(:nil?) { puts caller; false }
Here the double will return false on the first invocation and call the block on the second.
Reference for setting responses on expectations:
https://github.com/rspec/rspec-mocks#setting-responses

Resources