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.
Related
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
I have a class game which contains some arrays of custom objects (dinosaurs, cacemen etc.), that are returned by different accessors, such as game.dinosaurs, game.cavemen etc.
At present, all these accessors just return the internally stored arrays. But now I'd like to add some custom iteration methods to these arrays returned by those accessors, to be able to write code such as game.dinosaurs.each_carnivore { ... } etc. similarly to each_element and each_attr iterators in LibXML::XML::Node. But the objects returned from my accessors game.dinosaurs and game.cavemen have to behave like arrays still.
How are things like that usually done in Ruby?
Should I make the objects returned from my accessors to be some custom classes derived from Ruby's Array class? Or maybe should I just create a custom class with Enumerable mixed in?
I know I can use map or select externally on my collections, but I wanted to encapsulate these iterations internally that my class's users won't need to bother how to set up an iteration to select only carnivore dinosaurs from the internal array.
Edit: I'm not asking about how to use iterators or how to implement them, but how to add just some custom iterators to object which previously were just plain arrays (and still need to be).
It depends (as always). You could use an array subclass and you you could build a custom class and use composition and delegation. Here's a simple example with an array subclass:
class DinosaurArray < Array
def carnivores
select { |dinosaur| dinosaur.type == :carnivore }
end
def herbivores
select { |dinosaur| dinosaur.type == :herbivore }
end
def each_carnivore(&block)
carnivores.each(&block)
end
def each_herbivore(&block)
herbivores.each(&block)
end
end
And here's a simple one with composition and delegation:
class DinosaurArray
def initialize
#array = []
end
def <<(dinosaur)
#array << dinosaur
end
def carnivores
#array.select { |dinosaur| dinosaur.type == :carnivore }
end
def herbivores
#array.select { |dinosaur| dinosaur.type == :herbivore }
end
def each(&block)
#array.each(&block)
end
def each_carnivore(&block)
carnivores.each(&block)
end
def each_herbivore(&block)
herbivores.each(&block)
end
end
Both implementation can be used like this:
require 'ostruct'
dinosaurs = DinosaurArray.new
dinosaurs << OpenStruct.new(type: :carnivore, name: "Tyrannosaurus")
dinosaurs << OpenStruct.new(type: :carnivore, name: "Allosaurus")
dinosaurs << OpenStruct.new(type: :herbivore, name: "Apatosaurus")
puts "Dinosaurs:"
dinosaurs.each.with_index(1) { |dinosaur, i| puts "#{i}. #{dinosaur.name}" }
puts
But also has custom iterators:
puts "Carnivores:"
dinosaurs.each_carnivore.with_index(1) { |dinosaur, i| puts "#{i}. #{dinosaur.name}" }
puts
puts "Herbivores:"
dinosaurs.each_herbivore.with_index(1) { |dinosaur, i| puts "#{i}. #{dinosaur.name}" }
Output:
Dinosaurs:
1. Tyrannosaurus
2. Allosaurus
3. Apatosaurus
Carnivores:
1. Tyrannosaurus
2. Allosaurus
Herbivores:
1. Apatosaurus
You can do this via using ruby blocks. Read more
Simple example here:
class Game
def initialize
#carnivoures = [1,2,3]
end
def each_carnivoures
#carnivoures.each do |carni|
yield carni
end
end
end
Game.new.each_carnivoures{ |c| p c}
It also would be nice to have a possibility for chaining such filters. You can achieve this simply by wrapping select method into custom one, returning your new class instead of array. You may wrap some other methods as well, e.g. map:
class Units < Array
def select
self.class.new(super)
end
def dinosaurs
select{ |unit| unit.kind == 'dinosaur' }
end
def cavemen
select{ |unit| unit.kind == 'caveman' }
end
def carnivore
select{ |unit| unit.type == 'carnivore' }
end
def herbivore
select{ |unit| unit.type == 'herbivore' }
end
end
Units.dinosaurs.carnivore
Units.cavemen.herbivore
Interacting directly with brains is not easy, so I have a little Gateway Pattern in use with some Dependency Inversion.
NumberCruncher is a wrapper for my Brain class.
class NumberCruncher
def initialize brain = Brain.new
#brain = brain
end
def times_one_hundred *numbers
numbers.map &#brain.method(:multiply_by_100)
end
end
I'm getting an error when testing though:
NameError: undefined method `multiply_by_100' for class `Mocha::Mock'
Here's the test
class NumberCruncherTest
def setup
#brain = mock
#cruncher = NumberCruncher.new #brain
end
def test_times_one_hundred
#brain.expects(:multiply_by_100).with(1).returns(100)
#brain.expects(:multiply_by_100).with(2).returns(200)
#brain.expects(:multiply_by_100).with(3).returns(300)
assert_equal [100, 200, 300], #cruncher.times_one_hundred(1,2,3)
end
end
I'm assuming it's because of the &#brain.method(:multiply_by_100) call and mocha works by using method_missing or something. The only solution seems to be to change the setup
class NumberCruncherTest
class FakeBrain
def multiply_by_100; end
end
def setup
#brain = FakeBrain.new
#cruncher = NumberCruncher.new #brain
end
# ...
end
However, I think this solution kind of sucks. It gets messy fast and it putting tons of Fake* classes all over my tests. Is there any better way to do this with mocha?
I think you can fix your problem by changing your method.
from
numbers.map &#brain.method(:multiply_by_100)
# which is equivalent to (just to understand the rest of my answer)
numbers.map {|number| #brain.method(:multiply_by_100).to_proc.call(number) }
to
numbers.map {|number| #brain.send(:multiply_by_100, number) }
This is actually better because there are some issues with your code. Transforming an object method into a proc (as you are doing), kinda freezes the state of your object into the proc and so any changes on instance variables will not take effect, and probably it's slower. send should work fine on your case, and works with any mocking framework.
Btw, my guess on why your test does not work it's because mocha does not stub proc methods, and for good, because if you transform a method into a proc, you are not testing a method call anymore but a proc call.
And because everyone loves benchmarks:
#o = Object.new
def with_method_to_proc
#o.method(:to_s).to_proc.call
end
def with_send
#o.send(:to_s)
end
def bench(n)
s=Time.new
n.times { yield }
e=Time.new
e-s
end
bench(100) { with_method_to_proc }
# => 0.000252
bench(100) { with_send }
# => 0.000106
bench(1000) { with_method_to_proc }
# => 0.004398
bench(1000) { with_send }
# => 0.001402
bench(1000000) { with_method_to_proc }
# => 2.222132
bench(1000000) { with_send }
# => 0.686984
I have a Ruby class, and each method on it keeps indices of an array of hashes based on certain conditions.
For example (code has been edited since original posting)
module Dronestream
class Strike
class << self
...
def strike
#strike ||= all
end
def all
response['strike'] # returns an array of hashes, each individual strike
end
def in_country(country)
strike.keep_if { |strike| strike['country'] == country }
self
end
def in_town(town)
strike.keep_if { |strike| strike['town'] == town }
self
end
...
end
end
This way, you can do Dronestream::Strike.in_country('Yemen'), or Dronestream::Strike.in_town('Taizz'), and each returns an array. But I'd like to be able to do Dronestream::Strike.in_country('Yemen').in_town('Taizz'), and have it return only the strikes in that town in Yemen.
But as of now, each separate method returns an array. I know that if I have them return self, they'll have the method I need. But then they won't return an array, and I can't call, for example, first or each on them, like I could an array, which I need to do. I tried to make Strike < Array, but then, first is an instance method on Array, not a class method.
What should I do?
EDIT
Here is a part of my test suite. Following the answer below, the tests pass individually, but then fail.
describe Dronestream::Strike do
let(:strike) { Dronestream::Strike }
before :each do
VCR.insert_cassette 'strike', :record => :new_episodes
#strike = nil
end
after do
VCR.eject_cassette
end
...
# passes when run by itself and when the whole file runs together
describe '#country' do
let(:country_name) { 'Yemen' }
it 'takes a country and returns strikes from that country' do
expect(strike.in_country(country_name).first['country']).to eq(country_name)
end
end
# passes when run by itself, but fails when the whole file runs together
describe '#in_town' do
let(:town_name) { 'Wadi Abida' }
it 'returns an array of strikes for a given town' do
expect(strike.in_town(town_name).first['town'].include?(town_name)).to be_true
end
end
...
end
You can overwrite the method_missing to handle this.
Return self in your in_country or in_town method. Then when called first to it, delivery it to the all array to handle.
the code may be like this:
module Dronestream
class Strike
class << self
...
def all
...
end
def in_country(country)
all.keep_if { |strike| strike['country'] == country }
self
end
def in_town(town)
all.keep_if { |strike| strike['town'] == town }
self
end
...
def method_missing(name,*args,&block)
return all.send(name.to_sym, *args, &block) if all.respond_to? name.to_sym
super
end
end
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