Motley: I can't mock something that I have no control over (like third-party or old legacy code)
Maven: Design to interfaces, create a wrapper, and use tools for mocking. These are all great practices for leveraging mock objects.
[Context: Motley is struggling with creating mock objects for his unit tests]
Motley: Arrrrggggghhhh. You know, I'm really trying to do this TDD thing but it is quickly becoming a real pain in the butt. You said that tests have to execute quick. Trust me, I totally believe you on that front. I'm doing my best to make them execute quickly but some things just are not made to mock.
Maven: What's the problem?
Motley: I'm interfacing with Marty's code. I "new" the object up and call it. Unfortunately Marty's stuff takes quite a few seconds to execute. It's making my TDD experience as painful as looking at that shirt you're wearing.
Maven: What's wrong with my shirt? Anyway, the problem is that you are hard coding the instance creation of Marty's object. You are depending on an implementation, which is a violation of one of our key design principles we talked about.
Motley: Yeah, yeah, design to interfaces. But Marty has defined the interface and I have no control over it. No stubbing here, bubba!
Maven: Ah, but you do have some control! The best thing to do would be to collaborate with Marty and get him to expose a well-defined and documented public interface to his component. Then you can simply couple yourself to the interface and not the implementation.
Motley: Right! Then I can plug and play different implementations of the component, replacing Marty's version with my own stub.
Maven: You betcha! You got it, Mot!
Motley: But let's say Marty's component is actually a third party component that I have no control over.
Maven: You can solve any software problem with an extra layer of indirection. I'm kidding of course, but it does help here. In this case, wrap that component with a façade design pattern that provides an interface that you define. Then your core code depends on that façade interface instead of directly on the third party component.
Motley: Ok, fortunately that makes sense. You used the word "mock" previously but it just seems like a stub to me. What is the difference?
Maven: Don't worry too much about the nomenclature. A stub provides some sort of canned response to a call. Usually the response is hard-coded and there is no real logic associated with the result that is returned. A mock is similar to a stub, but it also verifies some kind of behavior. Did the client of the mock call it appropriately? Is the state of the object as we expect? Are return values from methods that we expected to be called set appropriately? The term "mock" goes along with TDD quite frequently, but you can get by effectively just using a stub if you wish.
Motley: So a mock does extra work on top of a stub. How do I get the mock to do this extra work of behavior verification? Do I have to write all the code myself?
Maven: You can, but you can also use a mock object framework. There are a few out on the Internet for various platforms. NMock is an example for .NET, but they're out there for other languages too. Generally the name is xMock, where the "x" is replaced by some kind of letter or phrase corresponding to the language or platform for which it is built.
Motley: I'm still not sold - what about all the wrappers I'll have to write to mock out bits of legacy code that were not designed with interfaces in mind?
Maven: Check out a free tool called TypeMock.NET. TypeMock allows you to mock any class in your system, including concrete classes. No changes are required to the production code. TypeMock automatically intercepts calls to the class(es) of your choosing and injects whatever behavior you specify in its place. It does this in one of two ways: (a) by recording expectations of the code in a similar way that you would record a macro; and (b) using reflection to look for calls to expected methods and replace them with alternatives. The key is running an executable that wraps your test harness such that it is able to hook the desired method calls.
Motley: Holy crap - that sounds incredibly cool!
Maven: About as cool as you are, with all the windows open in the winter.
Motley: Ouch. A dig from Mave. Actually, the only thing you have dug is your grave once we leave the office and I get my hands on you. In all seriousness, I can understand the mock story, but I'm still not sure about the best technique for mocking something out. I figured I would just hack in an "if" statement to give me the right object given the scenario - release or test.
Maven: Good question. A hacked-in hook could work, but it's not the best option. Why don't we go over some methods of injecting mock objects into your design. Let's go write some code on the whiteboard...
Maven's Pointer: To take advantage of the full power of mock objects, try a mock object framework like NMock. The Quickstart documentation for NMock contains a simple example of how to use the framework and what is meant by behavior verification.