I have two class OneClass
and AnotherClass
:
class OneClass
def initialize(*args)
@another_member = AnotherClass.new()
end
def my_method()
if @another_member.another_method1() then
@another_member.another_method2()
end
@another_member.another_method3()
end
end
I am getting to write unit for OneClass
.
How can I mock @another_member
?
Mocking is a technique in test-driven development (TDD) that involves using fake dependent objects or methods in order to write a test. There are a couple of reasons why you may decide to use mock objects: As a replacement for objects that don't exist yet.
A Double is an object which can “stand in” for another object. You're probably wondering what that means exactly and why you'd need one. This is a simple class, it has one method list_student_names, which returns a comma delimited string of student names.
Use let to define a memoized helper method. The value will be cached across multiple calls in the same example but not across examples. Note that let is lazy-evaluated: it is not evaluated until the first time the method it defines is invoked.
With the idea of Anthony, I make it work.
describe OneClass do
before(:each) { @one_object = OneClass.new }
describe 'my_method' do
it 'should work' do
mock_member = double
allow(mock_member).to receive(:another_method1).and_return(true)
@one_object.instance_variable_set(:@another_member, mock_member)
@one_object.my_method()
expect(mock_member).to have_received(:another_method1)
end
end
end
You can't mock an instance variable. You can only mock methods. One option is to define a method inside OneClass
that wraps the another_member
, and mock that method.
class OneClass
def initialize(*args)
end
def my_method()
if another_member.another_method1() then
another_member.another_method2()
end
another_member.another_method3()
end
private
def another_member
@another_member ||= AnotherClass.new()
end
end
However, you don't have to, there is a better way to write and test your code. In this case a better approach to mocking is to use a pattern called Dependency Injection.
You pass your dependency to the initializer.
class OneClass
def initialize(another: AnotherClass, whatever:, somethingelse:)
@another_member = another.new()
end
def my_method()
if @another_member.another_method1() then
@another_member.another_method2()
end
@another_member.another_method3()
end
end
(Note I used keyword arguments, but you don't have to. You can also use the standard args approach).
Then, in the test suite you simply provide the test object.
let(:test_another) {
Class.new do
def another_method1
:foo
end
def another_method2
:bar
end
def another_method3
:baz
end
end
}
it "does something" do
subject = OneClass.new(another: test_another)
# ...
end
There are several advantages of this approach. In particular, you avoid using mock in the tests and you really test the object in isolation.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With