How do you check the return value of a method called inside another method rspec?

3.5k views Asked by At

Let's say you have some class like

class Foo
  ...
  public def methodA
    x = methodB(true)
    # other operations (assume x is not the return value of methodA)
  end

  private def methodB(arg)
    if arg
      return 1
    else
      return 0
    end
  end
end

When you're writing a unit test for methodA, you want to check that x was assigned the right value, which means you have to check that the call to methodB returned 1 like you expected.

How would you test that in rspec?

Right now I have (with help from How to test if method is called in RSpec but do not override the return value)

@foo = Foo.new
expect(@foo).to receive(:methodB).with(true).and_call_original

But I don't know how to verify the actual return value of methodB.

2

There are 2 answers

2
Joel Blum On

I think instead of mocking you can just call the private method?

@foo = Foo.new
result = foo.send(:methodB, true)
expect(result).to eq 1

This is basically testing the private method directly. Some people frown upon doing that but if it has lots of logic it's sometimes easier to test it directly. I agree with @spickermann that it's usually best to test the public method and leave the implementation details of the private methods out of the specs.

10
Todd A. Jacobs On

Don't Test Multiple Methods in a Single Unit Test

You're going about this wrong, because you're creating an unnecessary dependency between two different methods. Instead, you should refactor your code so that:

  1. x is an argument to #methodA,
  2. x, @x, or @@x is a variable accessible to methodA that you can set to whatever expected value you want, or
  3. stub out #methodB for this unit test.

As an example of the last:

describe Foo do
  describe 'methodA' do
    it 'does something when methodB returns 1' do
      # stub the default subject, which is Foo.new
      allow(subject).to receive(:methodB) { 1 }
      expect(subject.methodA).to eq('foo bar baz')
    end
  end
end

This code is untested, because your posted code is not a complete, verifiable example. While you'd really be better off refactoring your code so you aren't testing nested depedencies at all, using test doubles or method stubs are certainly viable options.