That's Not Yours

That's Not Yours

Eric Smith
Eric Smith

October 27, 2011

My first exposure to mock objects was in C++, where they are practically indecipherable to the TDD newbie, so I didn't use them. That may have been a blessing in disguise because a year or two later, I learned the RSpec mocking framework and saw examples that looked something like this:


it "should update the attributes of the blog post" do
		mock_blog = mock_model(Blog)
		Blog.stub!(:find).with('9').and_return(mock_blog)
		
		mock_blog.should_receive(:update_attribute)
				.with({
						'title' => 'Eric',
						'text' => 'A unified theory'
				})
		post "update", {'id' => '9', 'title' => 'Eric', 'text' => 'A unified theory'}
end

This is a terrible way to use mocks and sadly I've written a lot of code that looks like it. The code to pass this test will crash because there is a mistake, update_attribute should be update_attributeS. This kind of test error often leads to embarrassing bugs in QA or worse production, and after enough of these defects many people turn away from mocks entirely. That's a shame because when used properly mocks are essential to crafting code with tests that run fast while expressing their intent. Let's take a look at one heuristic that can help avoid this kind of bad testing, while simultaneously improving the design.

Don't Mock What You Don't Own

The book Growing Object Oriented Software, Guided by Tests states that you should never mock interfaces that you don't own. When I read that it caught me off guard. After all faking third party libraries that talk to databases or the network is the primary use case of a mock object right?

Well no, not really. Mock objects are meant to improve the expressiveness of code while reducing coupling, and this test does neither. Compare the test above to the version written with real objects:


it "should update the attributes of the blog post" do
		blog = Blog.create!(:title => 'Not Eric', :text => 'a hackneyed theory')
		post "update", {
				'id' => blog.id,
				'title' => 'Eric',
				'text' => 'A unified theory'
		}
		blog.reload
		blog.title.should == 'Eric'
		blog.text.should == 'A unified theory'
end

Looking at this new test we can see that its at least as expressive as the original test with mocks, if not more so. A reader with little to no knowledge of ActiveRecord could read this test and understand what's going on but I doubt that's the case with the mocked version of the test. This code is coupled to ActiveRecord, but so is the mocked test because any object I use will have to match ActiveRecord's interface anyway. So I have accomplished neither of the goals of using a mocked object

What about the goals of TDD? We've already covered how the mocked out version of the test will pass with incorrect code, but many a TDDer has said, "TDD isn't about testing*, it's a design approach!" Well what design has been done in the mocked out test? Zero, zilch, nada, zip. The test has simply reproduced the ActiveRecord code I was about to write anyway. Really the only benefit the test above gives you is speed, and while I value fast tests more than the next guy I hardly think going faster is a good enough justification for a poor test.

* This statement drives me nuts. One of the other things TDDers say is, "With tests I can refactor safely, knowing I didn't break anything." Well if TDD isn't about testing you can't, can you? Of course TDD is about testing, it's just not only about testing and it's not the only testing you should do. Back to your regularly scheduled blog.

What is the alternative?

Interestingly you can write this test with mocks and have it express your design better if you don't use an ActiveRecord object:

it "should update the attributes of the blog post" do
		BlogEngine.should_receive(:update_entry).with({
				:id => '9',
				:title => 'Eric',
				:text => 'A unified theory'
		})
		post "update", {
				'id' => '9',
				'title' => 'Eric',
				'text' => 'A unified theory'
		}
end

What's important to understand here is that I would write this test whether or not BlogEngine exists, indeed I would prefer to write it before. Then I would write the BlogEngine injecting ActiveRecord objects into it. It still suffers from being a "double entry accounting" test where the test looks just like the implementation, but that's okay because the code will do almost nothing. The test also reflects the way I want to design the system, rather than polluting the system with a bunch of ActiveRecord information. It's not a perfect piece of code, in particular I don't like the class method, but it's moving in the right direction. Now when I write my tests on BlogEngine I'll use the real ActiveRecord objects where needed. My tests will speed up because I will minimize my contact with the database in the code rather than hiding it with tons of mocks.

A Networked Example

My first experiences in TDD required the network, because I was young and stupid and I was writing networking code. The tests flaked out on me constantly, and took way too long even when they did pass. They were filled with logic just to try and keep them running, and I started to need tests for the tests. If I'm not supposed to mock what I don't own, but I'm not supposed to talk to the network (or to a database, etc), then how do I properly test this kind of code? The approach I'm advocating improves testability and design.

Wrap the third party library

The code I wrote that required the network in tests was structured something like this:

As you can probably guess it was a simple system to monitor the status of various computers in a network. There was a network object, a computer object, and ...the computer depended on ping! Holy cow what's a system call doing in there? What idiot wrote that code? Let's change the code to use a simple wrapper:

By tying Computer to the ISonar interface instead of ping directly, it's simple to substitute in various types of sonar objects. In this case PingSonar represents Sonar using the ping command whereas FakeSonar is a fake one for testing. It is important to note that I've tied Computer to an interface instead of a concrete object, and even though many languages don't support interfaces as a construct all programming languages support them as a concept. In other words you can still depend on the interface to a class, through duck typing, rather than the concrete class even in a language like Ruby.

Test the wrapper using the third party library directly - IF POSSIBLE

Continuing with our example PingSonar needs to be tested. At this point we will want to test using the real ping command if we can. This may mean doing some tricks like pinging localhost, then changing the ping address to a known failed address. If that becomes too slow for the continuous build we may pull it out and run it in a separate build, or simply accept that the ping command is not testable and pull it further into it's own class that does nothing but delegate to the system call. Those are desperation measures. This test will be slower, which is one of many reasons why we don't want ping calls sprinkled throughout the code base.

Domain Language

When you wrap your third party object don't name it ThirdPartyObjectWrapper please. Step back and think about it in the context of your domain. What is it? What does it do? This is oftentimes difficult, and the temptation to name it ThirdPartyObjectWrapper will be strong. Don't give in! This is your opportunity to take an outside library and make it express your internal domain, take it!

A Limited Wrapper

On my first attempt at a mock object I took a class I needed to use, and tried to override every single one of it's methods. This comical attempt ended quickly, because the class was a C++ template with dozones of methods. A framework or third party object often has to provide a large interface to solve a large number of needs, of which you only need a subset. When you write your wrapper don't try and write everything it does, write the operations you need, with the interface you want. If you're using the language of your domain, rather than the language of the third party object, this is much simpler.

Summary

"Don't Mock What you Don't Own" is one of several heuristics I try and use when mocking objects in my own code. This particular one pays off immediately in the form of a codebase that is more reliable and expressive. Interestingly it dovetails nicely with a lot of the writing going on in the Rails community these days, but this is not strictly a Rails problem. Wrapping your third party libraries in objects, and then using those objects in your code (rather than the library directly) is just good practice.