Another in 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.
Testing Theories Part 1 - Classical vs Mock Testing
Testing Theories Part 2 - Unit Testing with Inversion of Control Pro's/Con's
Testing Theories Part 3 - IoC Containers (Castle Windsor, Spring.NET), AutoMocking Containers
Testing Theories Part 4 - TypeMock Isolator - The Dark Horse of Unit Testing?
A lot has been discussed on the pro's/con's of IoC for the purposes of unit testing and in general it has been framed as for or against TDD. This is actually not entirely correct. TypeMock Isolator has the notion of Reflective Mocks that allow setting up expectations on actual classes and not just interfaces. I've been fascinated with the idea of TypeMock and it introduces the concept that you don't HAVE to modify your design for testability using IoC, interfaces, and factories strictly for testing purposes. At the Philly .NET Code Camp in January the ALT.NET track was very heavily into testability via IoC. Also as part of that track, I attended Don's presentation on design patterns and I had asked him afterwards how he achieved testability in his sample code when he did not implement IoC and the design pattern implementation would have made it difficult to do so. He mentioned using TypeMock, a product I had been reading a lot about, and it definitely made me think of this product more seriously.
This article, Stop Designing for Testability by TypeMock Isolator evangelist Eli Lopian also promotes the bold assertion that IoC is not the only path to testability. The article itself is very thought provoking as well as the discussion in the comments. One particular comment by narsyseth (sadavoya) draws a great a parallel to white box and black box testing.
"Hi Guys,
This has been an interesting thread to watch. I see this
moving toward a general discussion on how to test. It seems to me that
there are two types of unit testing, white box and black box. Both are
equally useful at different times. Black box unit tests would test an
object using it's interface. The onus is on the production code to
provide the flexibility to inject mocks, but those mocks require
limited knowledge of what they are "mocking". White box tests would
involve mocks that have more intimate knowledge of exactly how the unit
they are "mocking" works. It seems to me that Typemock leans toward the
white box approach, while the use of factories is more black box. I
realize it's not so clear cut, but that's a pattern I'm seeing here. Regardless
of whether or not that's true, where I see something like Typemock
really shining is when writing tests for legacy code, when you have no
tests to begin with. Assuming the legacy code is not written in a
test-friendly way, using the black box approach is extremely difficult:
you can't refactor the legacy code to support mock injection because
without tests you can't be sure you won't break it. On the other hand
you can't inject mocks because the legacy code doesn't support it. Typemock
breaks this vicious circle, it seems, by allowing the injection of
mocks without touching the production code. This way you can write
tests, then safely refactor the production code. Voila, the legacy code
can now be brought into a testing cycle and the 21st century. For me, this is the most exciting aspect of Typemock."
Some detractors mention that since TypeMock will let you test nearly anything, you'll have no incentive to adhere to any of the solid principles that come along with testability via IoC such as designing to interfaces, isolation, lower coupling, and single responsibility. That simply doesn't seem to be a clear argument. Having a more flexible tool doesn't automatically make you a sloppier programmer. Ideally your own discipline should motivate you, not the limitations of a tool.
Roy Osherove, formerly on the fence about the power of TypeMock and now affiliated with it, also provides some insights on why TypeMock shouldn't be avoided just because it's flexible.
Personally, while I have done research and information gathering from various online sources and in-person conferences, there is no substitute for sufficient hands on experience with Unit Testing and TDD. I'm still new enough to it to have a
revelation that come about only when I was trying to retrofit testing into legacy code. Had I been using TypeMock, I very well may have just used Reflective Mocking and not realized that my design wasn't very well layered nor were my classes following single responsibility very well.
I do have some reservations about instituting a non-free component into our codebase. The cost is pretty reasonable. The main concern is that the community usage is less than free alternatives like Rhino Mocks so you're more likely to run into a blogger or another developer at a user group and discuss Rhino Mocks than TypeMock. The basic principles of mocking seem fairly similar though, especially with the Natural Mock support so I'm willing to give it a shot. There is a downside to the latest open source trends as well. They can be quickly replaced by the next big free thing.
For now I will be doing IoC and using non-reflective Mocks through both TypeMock and Rhino Mocks for a few features while I'm still getting the hand of it to get a good frame of reference on both. For legacy code that I am simply adding tests to because I'm not making significant feature changes (can't justify the full refactor and QA cycle of a rewrite), I'll use reflective mocks from Type Mock.
After I become more experienced in making testability design decisions, I'll consider which framework to use for new code as well.