0

Lets say I have the following module:

module SillyDemo
   class Monkey
     def screech(sound)
        sound
     end
   end

   class Ape < Monkey
     def process(sound)
       sound
     end
     def screech(sound)
       process(sound)
       super
       sound
     end
   end
end

And then the following minitest:

   require_relative 'sillydemo'
   require "minitest/spec"
   require "minitest/autorun"

   describe "Ape" do
        before do
           @ape = Ape.new
           @screech = "YEEEEEEE"
        end

        it "screeches" do
            @ape.screech(@screech)
            must_send [@ape, :process, @screech]
            must_send [@ape, :super, @screech]
        end 
     end

This errors out with:

NoMethodError: undefined method `super' for #<SillyDemo::Ape:0x007feeb10943c0>
    (eval):4:in `must_send'

I have also tried:

must_send [@ape, :"SillyDemo::Monkey.screech", @screech]

which errors out with:

NoMethodError: undefined method `SillyDemo::Ape.run' for #<SillyDemo::Ape:0x007fc5a1874e20>
    (eval):4:in `must_send'

My question is, how can I use minitest to test a call to super?

Abraham P
  • 15,029
  • 13
  • 58
  • 126

1 Answers1

0

In Ruby super is a keyword, not a method. Also, the must_send expectation isn't verifying that the method was called, it just verifies that the return value from the method is truthy.

http://www.ruby-doc.org/stdlib-2.0.0/libdoc/minitest/rdoc/MiniTest/Expectations.html#method-i-must_send http://www.ruby-doc.org/stdlib-2.0.0/libdoc/minitest/rdoc/MiniTest/Assertions.html#method-i-assert_send

Mocks are usually used to verify that a method was called. However, Minitest::Mock doesn't allow for this type of check very easily by design. Here is how you can do this though.

it "screeches" do
  # 1) Create mock
  sound_mock = Minitest::Mock.new
  sound_mock.expect :process, true, [String]

  # 2) Place mock
  @ape.instance_exec(sound_mock) do |sound_mock|
    @mock = sound_mock
    def process sound
      @mock.process sound
    end
  end

  # 3) Verify mock was called
  @ape.screech(@screech)
  sound_mock.verify
end

Pretty ugly, right? This is by design. Sort of like syntactic vinegar. The reason is that this use of mocks isn't very informative. It is checking the implementation of the code. You would like to be able to refactor the code without changing behavior and have the tests continue to pass. However, this test will very likely fail when the implementation is changed. To discourage folks from making this kind of mistake it was decided by the Minitest authors to keep this type of check difficult.

Other mocking libraries such as RR or Mocha make this type of check much easier.

blowmage
  • 8,854
  • 2
  • 35
  • 40
  • but this still only tests that process is sent, which I had working in my initial code sample, while not verifying that the superclasses method which is overriden was called by super.. – Abraham P Jan 11 '14 at 07:46
  • That is implementation. Using mocks to verify implementation is a bad idea™ IMHO. Test the behavior of your API instead. – blowmage Jan 13 '14 at 17:05