"Pure" Unit Tests with Mock Objects
Another big question people ask about unit testing revolves around how pure our unit tests need to be. Pure unit testing requires isolating an individual unit to validate its correctness.
So, for example, if our class interacts with external servers, we might create a stub to take the place of that server (such as a dummy server that simply listens on a port and returns the expected response). Our unit test can then validate our class using the stub rather than the real server. This enables us to validate that our class is doing its part correctly without depending on the external server acting right.
But what about if our class interacts with other classes? How do we factor them out? A popular idea is to use mock objects. Say we want to unit test our Training class, which contains a method enroll( Student ). Rather than passing it a real Student object, we could create a MockStudent object that implements the same interface as Student. We then tell MockStudent what methods we expect to be called, such as getFirstName() and getLastName(), and what to return for them.
Once we call Training.enroll( MockStudent), our mock object validates that every method we expected to be called was, indeed, called. We even have the option of requiring that these methods are called in a specific order. If any of the expected methods were not called, or if any unexpected methods were called, our test fails.
Well, I really like the idea of mock objects - up until that last little bit. We seem to be back to the same concern that I have with unit testing private methods, and that is who cares which methods in Student are called, much less in what order? All I care about is that the student was successfully enrolled in the training. Maybe Training.enroll() was only using the student's first and last name to set a unique ID and the developer later realized that it's a bad idea to have data in our key, so updated the method to use a GUID instead. Should the Training.enroll() unit test now break because that method is no longer calling Student.getFirstName()? I don't think so. And, again, I think we're setting up unnecessary barriers to refactoring.
So, this isn't meant as a "never use mock objects" anymore than a "never test private methods." Just a recommendation to consider your unit testing objectives and what's going to best serve them before blindly diving in to the latest cool thing you just read on some schmo's blog.
Here are some great resources if you'd like to learn more:
» The Flawed Theory behind Unit Testing (Michael Feathers)
» Testing Private Methods with JUnit and SuiteRunner (great stuff, even if you're not using these tools)
» Mocks Aren't Stubs (Martin Fowler)
Reposted from: The Hacker Chick Blog