{{{
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!*
}}}
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.
LikeLike
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.
LikeLike
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.
LikeLike
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”.
LikeLike
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:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 π
LikeLike
@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”…
LikeLike
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.
LikeLike
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?
LikeLike