What was a Classic Tester Became a Mockist…

{{{
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.

h3. 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”:http://www.rubyinside.com/dhh-offended-by-rspec-debate-4610.html.

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”:http://twitter.com/justinko happy) but right now, *this test framework still is _too powerful_ for me*.

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”:https://github.com/seattlerb/minitest code, included in Ruby 1.9 and working out-of-the-box.

h3. 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_”:http://martinfowler.com/articles/mocksArentStubs.html. 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.

h3. 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),
      "BE NICE."
  end

This could go on forever, and we test all the features that the method provides in numerous test cases – or @it@s – 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.

h3. 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.

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.

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!

h3. 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”:http://martinfowler.com/articles/mocksArentStubs.html, which you should definitely read if you haven’t, yet!

h3. Mocking frameworks?

I found “mocha”:http://mocha.rubyforge.org/ to be a very simple to use mocking framework integrating seamless into MiniTest. Never peeked at the source, though. “RSpec”:https://github.com/rspec/rspec comes with a powerful mocking functionality as well, which most people might know, use and appreciate.

h3. 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!*
}}}

Advertisement

8 thoughts on “What was a Classic Tester Became a Mockist…

  1. 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.

    Like

  2. 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.

    Like

  3. 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.

    Like

  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”.

    Like

  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:


    class MyClass
    def self.all_methods
    all_singleton_methods + all_instance_methods
    end
    def self.all_singleton_methods
    singleton_methods
    end
    def self.all_instance_methods
    instance_methods
    end
    end
    describe MyClass
    describe '.all_methods' do
    before do
    MyClass.stubs(all_singleton_methods: [:a], all_instance_methods: [:b])
    end
    it 'returns all singleton and instance methods' do
    MyClass.all_methods.should eq([:a, :b])
    end
    end
    end

    view raw

    gistfile1.rb

    hosted with ❤ by GitHub

    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 πŸ™‚

    Like

  6. @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”…

    Like

  7. 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.

    Like

  8. 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?

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s