There has been a lot of conflict about the Microsoft Fakes library recently. My coworker Jim Cooper posted VS11 Fakes Considered Harmful and VS11 Fakes Considered Harmful (part 2) on our company blog. Peter Provost, the program manager for Visual Studio, responded with Visual Studio Fakes Part 2 - Shims on his blog. Rich Czyzewski, systems architect at Fidelity, recently shared his thoughts on the library here Noninvasive Unit Testing in ASP.NET MVC4 – A Microsoft Fakes Deep Dive. I interacted briefly with Peter when he called Jim to discuss the library, but I haven't said much publicly. Today I posted the following on Rich's blog and decided to share it here too.
It is important to remember what automated testing is actually for. The primary goal it to get code that is maintainable. I think that the Fakes library, like Type Mock and Just Mock** before it, is a very powerful tool for dealing with poorly-structured, legacy code. Unfortunately, like any very powerful tool, they are also dangerous in general use. No one would use a 16-penny, framing air-nailer to assemble a kitchen spice rack, for example.
Allow me to describe what I feel is a proper use of a tool such as Fakes (borrowed at least in spirit from Michael Feathers):
1. Decide to add a feature to an untested big-ball-of-mud.
2. Introduce tests (vice grips) around a tightly coupled class that ensure it's current behavior.
3. Refactor mercilessly; extracting methods and classes and introducing interfaces until you have well factored code that conforms to common design principles like loose coupling, high cohesion, single responsibility, and dependency injection. Ensure that the vice grip tests continually pass. Introduce new unit tests around each new component ensuring their functionality.
4. Once all the code is refactored properly and is well tested and the vice grips still pass, remove them as the were only needed to hold the project together while it was in motion and the new, more-focused unit tests are now guaranteeing the functionality of the whole. (See JBrains - Integration Tests are a Scam for more details on why they must be removed.)
5. Add the new feature to the well factored, clean code.
6. Repeat from 1 on the next block of messy code I need to add a feature to.
You will notice that the use of the Fakes is transient, not a permanent part of the test suite. Once I have properly factored the code, I have no need to deep dive into the CLR or private members to manipulate them directly.
Unfortunately, I do not see most teams I work with moving past step 2. Once the code has tests, most developers consider their work done and they move to the next feature. After all, the test coverage metric has been met, right? Even if there isn't schedule pressure, unwinding a tightly coupled mess is a challenge that many developers avoid, in my experience because adding features is more visible to stakeholders. But if you aren't refactoring the code to introduce additional tests and testing seams after the vice grip tests are in place, you are missing out on the primary benefit of libraries like Fakes or Type Mock. You will be left with code that is just as messy and difficult to deal with the next time you need to modify it. You will find that the shortcuts you took by using Fakes didn't make you job at all easier when you return to that code.
As Joe [Eames of http://www.testdrivenjs.com/] implied [in a previous post], the expressiveness of code is paramount to long term maintenance. We have all learned that code is written once, but ready many, many times. Using terms from this application's domain rather than another domain is a key to maintaining the code in the long term. I too strongly advocate isolating "my" code from "their" code by building up an anti-corruption layer using thin(ish) adapters. This allows me to change my code at will without worrying about how they express their domain.
As was mentioned before, there are many positive pressures put on a code base by the introduction of tests. Small, focused classes with lower cyclomatic complexity are much more straightforward to test. Explicit injection of dependencies makes using a class in a context other than production much easier. Relying on interfaces rather than implementations helps focus the code as well on behaviors. The pressures that push us to these features in the design disappear when you can Fake or shim at the CLR level. This will lead to more poorly factored code. My current team consists of very skilled designers and developers. Even for us, testing regularly exposes areas of the code that are more coupled and less flexible than we suspected. We then refactor the code to better designs and as a side effect, we get to add the desired tests.
* As a side note, in my experience, the static keyword is a code smell in an OO language as it leads to more concrete type coupling, which again makes maintenance more difficult.
** I always felt that the price of these tools was a feature rather than a drawback as that keeps teams who don't absolutely need them from reaching for these tools. Alas, Fakes ships with VS11.