Rspec test fails - ruby

I am trying to test #game_over? method in which it calls #check_row in Board class using an instance_double but the test fails. Here is the test:
describe '#game_over' do
subject(:game_over) { described_class.new }
let(:board) { instance_double(Board) }
context 'when #game_over is called' do
it 'calls #check_row in Board' do
game_over.game_over?
expect(board).to receive(:check_row)
#game_over.game_over?
end
end
end
I was expecting the #game_over? to call #check_row in Board class but the test fails. Here is the method I am testing:
def game_over?
return true if #board.check_row || #board.check_column || #board.check_diagonal || #board.check_antidiagonal
false
end
Here is the failure message:
1) Game#game_over when #game_over is called calls #check_row in Board
Failure/Error: expect(board).to receive(:check_row)
(InstanceDouble(Board) (anonymous)).check_row(*(any args))
expected: 1 time with any arguments
received: 0 times with any arguments
Here is my Game #initialize method:
def initialize
#board = Board.new
end

The instance of Board in your Game class and the board mock in your test are different instances and therefore the test fails.
I suggest using dependency injection to be able to control the board instance in the Game and change your initializer and your test like this:
# in the Game class
def initialize(board = nil)
#board = board || Board.new
end
# in your spec
describe '#game_over?' do
subject(:game) { described_class.new(board) } # inject the board stub here
let(:board) { instance_double(Board) }
before { allow(board).to receive(:check_row).and_return(true) }
it 'delegates to Board#check_row' do
game.game_over?
expect(board).to have_received(:check_row)
end
end
Note:
I would argue that the test in its current form doesn't add much value and that it tests an internal implementation detail that you should not really care about.
There is no benefit in testing that a specific method is called on an internal #board object in the Game. In fact, testing such internal behavior will make it more difficult to refactor the code later on when requirements changed or new features are implemented.
Instead, I suggest focusing on testing that the method returns the expected result under certain preconditions (but not if and how the result is received from another object).
In this example, I suggest not testing that board.check_row was called, but that Board#game_over? returns the expected result because of the call) like this:
# in the Game class
def initialize(board = nil)
#board = board || Board.new
end
# in your spec
describe '#game_over?' do
subject(:game) { described_class.new(board) } # inject the board stub here
let(:board) { instance_double(Board) }
context 'with the board row check returns true' do
before { allow(board).to receive(:check_row).and_return(true) }
it 'is game over' do
expect(game).to be_game_over
end
end
end

Related

How to write rspec for method inside which other class object is instantiated

I want to write rspec for class A's test method in ruby
class A
def test
b = B.new
b.run!
end
end
class B
def run!
return 1
end
end
Can someone please tell how can I do this using mock
You can do something like this:
let(:a) { A.new }
let(:b_mock) { instance_double(B, run!: 'result') }
describe '#test'
it 'instantiates B and calls #run!' do
allow(B).to receive(:new).and_return(b_mock)
a.test
expect(b_mock).to have_received(:run!)
end
end
Essentially you want to "spy" on the created instance, and check that it has received the method you expected.
You could also just test that A#test returns 1, however, such a test is really verifying the behaviour of B#run!, meaning your tests are coupled.
b = double(:b, run!: nil)
B.stub(new: b)
expect(b).to receive(:run!)
A.new.test

How do I use MiniTest::Mock to assert that a 'verify' method has been called on a mocked object?

I have a class that defines a 'verify' method which is unfortunately the same name that MiniTest::Mock uses to verify a method has been called. I'm running into a clobbering problem.
I have classes defined as below.
class Reader
def initialize(verifier)
#verifier = verifier
end
def verify(subject)
#verifier.verify(subject)
end
end
class Verifier
def verify(subject)
subject != nil
end
end
I have tests setup as follows.
class TestReader < MiniTest::Test
def test_reader_when_verification_fails
mock_verifier = MiniTest::Mock.new
mock_verifier.expect :verify, false
reader = Reader.new(mock_verifier)
reader.verify(nil)
# The following verify method ends up being the 'verify' defined on
# Verifier, not on MiniTest::Mock. It blows up because Verifier#verify
# expects an argument.
mock_verifier.verify
end
end
How do I get around this?
EDIT: Original post (at bottom) was incorrect.
A working solution is:
#mock_verifier.instance_eval {
def assert
#expected_calls.each do |name, expected|
actual = #actual_calls.fetch(name, nil)
raise MockExpectationError, "expected #{__call name, expected[0]}" unless actual
raise MockExpectationError, "expected #{__call name, expected[actual.size]}, got [#{__call name, actual}]" if
actual.size < expected.size
end
true
end
}
* Below is incorrect *
Open up the mock, save off the MiniTest::Mock#verify method under a different method name (The -> Proc is needed to capture scope), then un-define 'verify' on the mock.
def #mock_verifier.assert
-> { #mock_verifier.method(:verify) }
end
#mock_verifier.instance_eval 'undef :verify'
Now at the end you do
#mock_verifier.expect :verify, false
#reader.verify(nil)
#mock_verifier.assert

Call method on variable defined in :let clause in Rspec before all test cases?

I'm working on a chess program and trying to write tests for the Board class. The top of the spec file contained the following code:
describe Board do
let(:board) { Board.new }
let(:empty_board) { Board.new(empty=true) }
...
end
However, I read that having boolean flags for methods is a code smell because it signifies that the method is responsible for more than one thing. So, I refactored the logic in the initialize method out into two methods in the board class: create_default_board which initializes the contents of the board to the default configuration, and create_empty_board.
In the spec file, however, I can't figure out how to call these methods on board and empty_board, respectively, before the individual tests are run without having to do so within each describe block. Is there a way around this?
Factory Methods
From your description this sounds like your specs are giving you feedback on the design. Based on your description:
I refactored the logic in the initialize method out into two methods
in the board class: create_default_board which initializes the
contents of the board to the default configuration, and
create_empty_board.
To me that sounds like you are creating factory methods which would "let" you write:
let(:default_board) { Board.create_default_board }
let(:empty_board) { Board.create_empty_board }
Using Object#tap
The tap method yields the object to a block, then returns that same object from the block.
let(:default_board) { Board.new.tap { |b| b.create_default_board } }
let(:empty_board) { Board.new.tap { |b| b.create_empty_board } }
Convert the Constructor Into a Builder
Some people dislike how the tap pattern looks. You can achieve the same pattern in a cleaner manner by yielding in your initializer:
class Board
def initialize
# ... your configuration ...
yield self
end
end
This allows you to write:
let(:default_board) { Board.new { |b| b.create_default_board } }
let(:empty_board) { Board.new { |b| b.create_empty_board } }
Use Named Parameters in Ruby 2.x
An alternative to passing positional boolean flags is to pass named boolean flags. Since the parameter is named, it is clear what the caller is intending to do:
let(:default_board) { Board.new }
let(:empty_board) { Board.new(empty: true) }
Inject the Configuration
Based solely on your description it is unclear what differentiates a "default" board from and "empty" board. Is it the dimensions of the board? Is this a 1-D, 2-D, 3-D, 4-D, or N-D board? Is it objects on the board? What are the default objects? Where are they located? Is it both of these?
By answering these, and likely other, questions you can flush out what parts of the board define the setup. For the sake of this example, and simplicity, let's say the board is one-dimensional and is defined by a size and a list of occupied indices:
class Board
def initialize(size, occupied_locations)
end
end
From this you could write:
let(:default_board) { Board.new(4, [0]) }
let(:empty_board) { Board.new(4, []) }
# or perhaps an empty board has no size
let(:empty_board) { Board.new(0, []) }
If there is a clear "default" you can provide default values for the parameters:
class Board
def initialize(size = 4, occupied_locations = [1])
end
end
Turning your lets into:
let(:default_board) { Board.new }
let(:empty_board) { Board.new(4, []) }
Combining All the Above
Of course there's nothing stopping you, except maybe desiring to limit the API scope or adding a first class BoardConfiguration object, from writing:
class Board
def self.create_default_board(&builder)
new(size: 4, occupied_locations: [1], &builder)
end
def self.create_empty_board(&builder)
new(size: 1, occupied_locations: [], &builder)
end
def initialize(size:, occupied_locations:)
# ... your configuration ...
yield self
end
end
This essentially allows you to write any of the previous let statements.
Which of these methods is the best for your application, and specs, is really up to you. Everything is a balance of trade-offs.
You can write a before block outside the describe block of each individual method that you are testing.
describe Board do
let(:board) { Board.new }
let(:empty_board) { Board.new } # empty=true
before(:each) do
board.create_default_board
empty_board.create_empty_board
end
describe '#a_board_method' do
subject(:a_board_method) { board.a_board_method }
before do
# Some other code here. It will be executed after the
# first `before` block
end
# Finally the test, which is executed after all `before` blocks
it { is_expected.to eq(42) }
end
end
The first before block will be executed right before every assertion block you have. If you have more before blocks inside the describe blocks of individual methods, those blocks will be executed after the "main" before block.
You can check more about that here. There is a version for RSpec 3.2 here.
Regarding the class responsibilities, seems that your Board class could be two separated classes: EmptyBoard and Board. It would be easier to test them too. You could have that "initialization" code for each "strategy" right inside the initializer method of the specific class.

Validate response from a yielded block

Assume I am testing the following class:
class Processor
def initialize(tree)
#tree = tree
end
def process(entity)
#tree.each_branch do |branch|
branch.inject({}) do |result, fruit|
result[fruit.name] = fruit.type == entity.type
end
end
end
end
I'd like to inject a stubbed tree, in my spec I would have:
describe Processor do
let(:tree) { double("tree") }
let(:apple) { Fruit.new("apple") }
let(:processor) { Processor.new(tree) }
let(:fruit1) { Fruit.new("orange") }
let(:fruit2) { Fruit.new("apple") }
it "should process a fruit"
tree.stub(:each_branch).and_yield([fruit1, fruit2])
Processor.process(apple)
end
end
I would expect the following hash to be created in the block. How do I verify that it is created correctly and returned to the caller of the block?
{ "orange" => false, "apple" => true }
EDIT: I omitted details of the Fruit class, it should be irrelevant.
If you're ever having to try and catch the result somewhere in the middle of a method that your testing, it's normally a good sign that you need to refactor.
Here's an example: add a method to the branch, then test the branch class (assuming it's a class that you're in control of).
class Branch
def unique_fruits
inject({}) do |result, fruit|
result[fruit.name] = fruit.type == entity.type
end
end
end
class Processor
# snip ...
def process(entity)
#tree.each_branch do |branch|
branch.unique_fruits
end
end
end
That's easier to test, as inject returns the hash. You can write a unit test for the branch class and isolate that method. Then in the Processor#process method you replace the inject block with a call to branch.unique_fruits.
If you don't have control over branch, just extract the block to another method on the Processor class:
class Processor
# snip...
def process(entity)
#tree.each_branch do |branch|
unique_fruits_on_branch(branch)
end
end
def unique_fruits_on_branch(branch)
branch.inject({}) do |result, fruit|
result[fruit.name] = fruit.type == entity.type
end
end
end
However, you can probably see that it doesn't look as nice - the Processor class is working at different levels of abstraction. But both of these are easier to unit test.

How to expect a method call on a newly created test double?

Say I have code like this:
class Car
def test_drive!; end
end
class AssemblyLine
def produce!
car = Car.new
car.test_drive!
end
end
Now, using RSpec I want to test/spec AssemblyLine without exercising Car as well. I hear we don't do dependency injection in Ruby, we stub new instead:
describe AssemblyLine
before do
Car.stub(:new).and_return(double('Car'))
end
describe '#produce'
it 'test-drives new cars' do
the_new_instance_of_car.should_receive(:test_drive) # ???
AssemblyLine.new.produce!
end
end
end
The problem, as you can see, is with the_new_instance_of_car. It doesn't exist yet before produce is called, and after produce returns it's too late to set any method call expectations on it.
I can think of a workaround involving a callback in the stubbed new method, but that's rather hideous. There must be a more elegant and idiomatic way to solve this seemingly common problem. Right...?
Update: here's how I solved it.
describe AssemblyLine
def stub_new_car(&block)
Car.stub(:new) do
car = double('Car')
block.call(car) if block
car
end
end
before { stub_new_car } # to make other tests use the stub as well
describe '#produce'
it 'test-drives new cars' do
stub_new_car { |car| car.should_receive(:test_drive) }
AssemblyLine.new.produce!
end
end
end
You can set an expectation on the test double:
describe AssemblyLine do
let(:car) { double('Car') }
before { Car.stub(:new) { car } }
describe "#produce" do
it "test-drives new cars" do
car.should_receive(:test_drive!)
AssemblyLine.new.produce!
end
end
end
You can also call any_instance on the class (as of RSpec 2.7, I think):
describe AssemblyLine do
describe "#produce" do
it "test-drives new cars" do
Car.any_instance.should_receive(:test_drive!)
AssemblyLine.new.produce!
end
end
end

Resources