10

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?

3 Answers 3

8

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
Sign up to request clarification or add additional context in comments.

Comments

5

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.

Comments

0

You can mock @another_member indirectly by stubbing AnotherClass.new:

another_member_double = double()
allow(AnotherClass).to receive(:new).and_return(another_member_double)

expect(another_member_double).to receive(:another_method1).and_return(somevalue)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.