Determining the Scope of What to Test Inside Methods
14 Apr 2017Sometimes it can be difficult to determine what you want to test inside a method, especially if that method calls other methods, which in turn call other methods and so on. For example, take the example class Cake below. It has a method called make which calls two methods mix_ingredients and bake, which in turn call Foo.mix and Foo.bake respectively.
class Cake
def make
mix_ingredients
bake
end
def mix_ingredients
Foo.mix
end
private
def bake
Foo.bake
end
end
class Foo
def self.mix
end
def self.bake
end
end
If we were to write a test for make, certainly we would want to test that mix_ingredients and bake get called.
describe Cake do
describe '#make' do
it 'calls mix_ingredients and bake' do
expect_any_instance_of(described_class).to receive(:mix_ingredients)
expect_any_instance_of(described_class).to receive(:bake)
described_class.new.make
end
end
end
However since mix_ingredients calls Foo.mix and bake calls Foo.bake would we also want to test that those get called in our test for make? The answer is it depends on whether these get called inside public or private methods.
Taking a look at mix_ingredients we see that it’s a public method. Since all public methods should be tested, we know that we will have a separate test for it and it will check if Foo.mix is called. Here is our updated test file with the test for mix_ingredients.
describe Cake do describe '#make' do it 'calls mix_ingredients and bake' do expect_any_instance_of(described_class).to receive(:mix_ingredients) expect_any_instance_of(described_class).to receive(:bake) described_class.new.make end end describe '#mix_ingredients' do it 'calls mix on Foo' do expect(Foo).to receive(:mix) described_class.new.mix_ingredients end end end
But what about bake? Since we don’t test private methods we won’t have a test for this. However, we still want to test that it calls Foo.bake. Since bake gets called when make gets called, we need to add a test for this in our make test.
describe Cake do describe '#make' do it 'calls mix_ingredients and bake' do expect_any_instance_of(described_class).to receive(:mix_ingredients) expect_any_instance_of(described_class).to receive(:bake) described_class.new.make end it 'calls bake on Foo' do expect(Foo).to receive(:bake) described_class.new.make end end describe '#mix_ingredients' do it 'calls mix on Foo' do expect(Foo).to receive(:mix) described_class.new.mix_ingredients end end end
In summary, determining the scope of what to test inside of a method that calls other methods, depends on whether public or private methods get called within it. If a public method is called, then it will have it’s own test and you only need to test that this public method gets called. If a private method is called, then it won’t have it’s own test, so you’ll need to test what’s happening inside the private method.