Today I had an interesting experience which I’d like to share with my friends. No, I didn’t discover my dark side, I knew that before. What I found out is that mocking as a test method comes in handy once you get it.

Why Don’t You Use RSpec?

I’ve never been an RSpec fan. Not that I don’t like the BDD-approach, which I do appreciate, I was always scared about its internal complexity. I happened to stay with MiniTest, and this has nothing – and i mean nothing – to do with the opinion of Mr. Rails.

Now, I don’t say that RSpec sucks and is too complex. This would be bullshit and I might even soon switch to using this powerful framework (just to make my friend Justin happy) but right now, this test framework still is too powerful for me.

1
2
3
4
5
6
7
8
9
10
class StringTest < MiniTest::Spec
  describe "String" do
    before do
      @string = "Be nice."
    end
 
    describe "#to_xml" do
      it "returns a string" do
        assert_kind_of String, @string.to_xml
      end

That’s pure MiniTest code, included in Ruby 1.9 and working out-of-the-box.

Testing with Assertions Is Not A Crime!

What MiniTest::Spec gives me is basically a BDD interace for classical testers. I say classic as I learned that testers not using expectations are called classic. Old school. Yo.

I can use the keyword methods describe and it to phrase my requirements to the object (line 2 and 8). Before and after hooks provide convenient setup and teardown blocks (line 3).

However, I still use assertions rather than matchers as found in RSpec. I could use matchers, though. MiniTest provides these.

  it "returns a string" do
    @string.to_xml.must_be_kind_of String
  end

Wow, this is “better readable” in many people’s opinion. I still prefer assertions. But that’s not the point of this essay. What I was going to talk about is mocking. Right, mocking.

Who needs Mocking?

Now let’s say my #to_xml method accepts an additional parameter to configure the behaviour. We’d have to test that, too.

  it "accepts the uppercase option" do
    assert_equal @string.to_xml(uppercase: true),
      "<STRING>BE NICE.<STRING>"
  end

This could go on forever, and we test all the features that the method provides in numerous test cases – or its – uppercase, lowercase, reverse, whatever.

Until now, I’ve been testing only using state verification. I run the method and test the state of my string instance afterwards using assertions. This is called state verification.

Purposeless, I now decide that my app needs another method. Here’s the implementation.

class String
  def to_exemel(options={})
    log "stupid phonetic alias called"
    to_xml(options)
  end

I told you, it doesn’t make any sense. What I want to point out is, the method #to_exemel delegates its arguments to the #to_xml method, which we already tested thoroughly.

Expect the Unexpected!

Testing the logging makes sense, I already use mocking here, as I mock the #log method. More on that in a minute.

1
2
3
4
5
6
describe "#to_exemel" do
  it "delegates to #to_xml" do
    String.any_instance.expects(:log)
 
    @string.to_exemel
  end

However, testing all the behaviour of #to_xml again doesn’t. Here, I found out that mocking is cool, too.

1
2
3
4
5
6
7
8
describe "#to_exemel" do
  it "delegates to #to_xml" do
    @string.
      expects(:to_xml).
      with(:uppercase => false)
 
    @string.to_exemel(:uppercase => false)
  end

First, we define expectations: In the ongoing test case, the #to_xml method must be called on the @string instance along with the specified options hash (line 3-5).

I then call the actual method which is subject of our test. The method will be run – and the mocking framework internally checks, if the #to_xml method is really invoked the way we want it (line 7). If it doesn’t, the test will fail!

I Never Knew It’s Called Behaviour Verification!

What we do here is behaviour verification – we no longer have an assertion checking the final string state (we could do that additionally) but set up an expectation, which changes the workflow of our test slightly.

  • I saved a lot of lines of test code since I do not assert all of #to_xml’s features in the #to_exemel method, again. This is cool.
  • Nevertheless, after mocking my test case includes a lot of knowledge about internal implementation. The test now’s aware that #to_exemel forwards to #to_xml. Changing this implementation detail within the class will break the expectation tests, although the second method might still work from the public perspective.

It seems to be a matter of careful choice when to use mocking, and when not. I for myself still lack the experience to make smart assumptions here. However, I can call now myself a Mocker according to Martin Fowler’s great post, which you should definitely read if you haven’t, yet!

Mocking frameworks?

I found mocha to be a very simple to use mocking framework integrating seamless into MiniTest. Never peeked at the source, though. RSpec comes with a powerful mocking functionality as well, which most people might know, use and appreciate.

Outsights?

The example was stupid, I hope I still could point out the difference between status verification and behaviour verification in this short post. Let me know what you think about it. Good nite, and Mock’n‘Roll!

Tags: ,

8 Responses to “What was a Classic Tester Became a Mockist…”


  1. Matt Briggs

    Mocking came out of a OO heuristic called “Tell, don’t ask” (http://pragprog.com/articles/tell-dont-ask), meaning your objects should either do things, or tell other things to do things, they shouldn’t query other things and then react based on what they find.

    When you write that kind of code, the interactions between your objects become very obvious, because you are not exposing things like state outside of the interface your object provides.

    When you mock, you are describing the interactions between objects in your system. If you are thinking about it that way, certain types of things become way clearer, because when your mocks become hard to write, maintain, or understand, it is a great indication of a problem with your design. It also makes your unit tests way more clear, because they tend to become more focused on a single thing.

    I would highly recommend reading “Growing object oriented systems, guided by tests”. I read it after watching how Gary Bernhardt codes in his peepcode screencast, and realizing I really didn’t understand how mocking is supposed to work in anything but the most superficial of ways. Its a java book, but IMO one of the best books written on the topic of mocking, and how it relates to OO.


  2. Staszek

    I have used this approach for long time in one of my projects and from my experience I can say it looks nice at the begining but it is pain in the ass after some time. Few short examples when tests like those sucks:

    When you change to_xml method to require more arguments, then you will have broken code and working test for your to_exemel method. Also when you would like to remove to_xml method then it is hard to find releated code you could also remove or change.


  3. Justice

    I’m not sure using behavior verification to verify query-type methods is all that great.

    Behavior verification is great for a command-type method, that is, a method which performs commands against other objects. Examples include: a method that should call FileUtils.mkdir_p, a method that should call @my_ar_instance.save!, etc.

    But it’s not that great for query-type methods, that is, methods which return results but do not perform commands. These types of methods should be described by specifying the setup and the inputs, and then checking the outputs, but not by verifying the internal operation of the method.

    To test #to_exemel, you can just check that the output of #to_exemel is equal to the output of #to_xml in a representative sample of cases. This is just input/output checking, and does not involve behavior verification.

  4. Hello Nick,

    I have a comment about which programming artifacts should be mocked. It is highly recommended to not mock methods of the same object. Mocking tdd is about collaboration BETWEEN objects/roles. It is not about implementation details of the “system under test”.

  5. Hi bro, nice post. Thanks for the link.

    This is a tough one, but I do believe there are 100% valid use cases for the “behavior verification” approach. Here is one:

    https://gist.github.com/1396448

    All “.all_methods” does is concatenate two arrays! Why do I need to go through the pain of “setting up” when the behavior is so trivial?

    Regarding the complexity of RSpec. Version 3 will make all “Object pollution” optional :)


  6. nick

    @Matt: Thanks man, you keep adding valueable comments to my blog! Will checkout that book you mentioned, although I don’t like reading entire books.

    @Staszek! Good to hear from you my friend! I agree that mocking shouldn’t be overused, but I guess it sometimes even makes the test easier to follow. Imagine I would completely test the #to_exemel method with all its features (see next comment).

    @Justice: Ok, I basically see your points. However, if I test #to_exemel using a “representative sample case” I’m just doing a random sample which may not entirely assert the scope of the method. If I use mocking, I really can describe what this method does (i.e. delegating everything to #to_xml) – I guess I need to get more experience with this approach, whatsoever…

    @Mitin: Good point. My article doesn’t spot at the “typical” use of mocking (i.e. specifying interaction between objects), but I use it to assert the delegation. As I said, the example sucks ;-)

    @Justin: My love. Wow, I really like the RSpec API. Your example is good, the more I think about it, the more I see how mocking can help avoiding the pain of “setting up”…


  7. Nathan Zook

    I agree that it is dangerous to mock methods in the same object, as Mitin and Staszek indicate. Mock at the boundaries & use integration tests.

    Thing is, it is a MAJOR pain to test out all of the public interface when it includes a bunch of wrapper methods. The tests are not dry. And when the wrapped method’s signature changes they break. All of them. Goody.

    One of the cool things about rspec is its scoping. And using loops to define test methods. I’ve taken flak about doing it, but the tests are maintainable.


  8. Poul

    Hi! Your example in RSpec:

    describe String do
    let(:string) { ‘Be nice” }
    it { string.to_xml.should be_kind_of String }
    end

    Is it still hard to read?

Leave a Reply