I'll begin a series of posts that in no small part will serve as summaries/reminders to myself on various readings on the topic of automated code testing. Perhaps others will find them useful though. I can't take credit for any terribly creative conjectures in my conclusions/summaries. They almost exclusively come from the writers in the referenced material. I would strongly encourage reading the original source materials for far more details and a better understanding of the topics.
Classical Testing favors using real objects when possible and stubs/
doubles when using the real code is not practical due to complex dependencies, slowness, resource availability, etc. In this way, it borders on integration testing more so than unit testing. You are testing more "real code" in each test. Care must be taken to not make tests too coarse grained, or at least also provide fine grained tests to reduce confusion and the time taken to determine the root cause of test failures. There is more of a tendency for coarse grained tests with classical testing than mock testing, but it is up to the diligence of the programmer to do classical testing well.
In some ways your unit tests are less fragile when refactoring than tests strewn with many mocks which can pepper your tests with implementation details when ideally the tests should know/rely on as little implementation knowledge as possible. This is somewhat reduced by using
Dynamic Mocks which only need to set expectations for the code being tested. Conversely, there maybe be some degree of implementation detail needed to make a stub/double so classical testing is not immune from this potential pitfall either.
Mock Testing, especially using a toolkit such as
Rhino Mocks or
TypeMock Isolator, really limits the amount of real code dependencies needed to run a test trending towards finer grained tests. Using an interface (or in the case of TypeMock just the class being tested itself), expectations for method calls, property setting, and return values can be defined and verified. While faking return values can be done quite easily with a stub as well, it would take a fancy stub to be able to verify that a method was simply called and this is trivial and compactly done with mocks. This amount of fine grained control and rigorous testing comes at the price of needing to know and specify the method names and their behavior in the implementation in your test code. Older frameworks relied on mocking using string parameters for class names but type safety has been improved in the latest frameworks and generics support in .NET has helped in this area as well.
---
There is absolutely room for both methodologies even within the same
project. For instance, while it may not be desired as part of your run on every compile test suite, a layer of integration tests in the classical style using as much real code as possible would make an excellent verification that your objects are truly interacting properly in context.
----
References