Skip to main content

Mock Roles not Types

"if it feels wrong, it probably is" - numerous Codeweavers' developers

The framework we use at Codeweavers is the excellent Moq, therefore when something is difficult to mock we are forced by the framework to write an adapter. We use an interface for testing, then create a concrete type which simply invokes the hard to test code such as static code, third party libraries and resources that are expensive to set up. There are some ways ways in C# to get around this, but they involve black magic and should be avoided at all costs unless you are deeply entangled in legacy code. A refactoring would be preferable over hard to test code.

The process of writing an adapter around hard to test code is a standard practice, we do it all the time as we are forced to by the unit testing framework. Some frameworks we use at Codeweavers such as ASP.NET MVC are designed with testability in mind, so unlike scenarios where you cannot test code easily, the MVC framework makes it possible. In a recent feature myself and a fellow colleague wrote some code within a controller which relied on some of the controllers' (the MVC framework) internals.

Rather than abstracting this into a class which we inject to make testing easier we went the route of setting up a complex, messy and tedious routing test fixture. Why you ask? Maybe it was the fact it was possible to test. Had it been straight up impossible or much harder, then introducing an abstraction would have been the obvious solution. The code in question was a small method that depending on the somewhat complex and unique routing values performed on a certain response. Fast forward a week later and the feature is to be expanded.

We were back were we started, the new feature needed more setup that relied on the framework, and in turn once this production code was changed, the old test fixture would need updating. The very thought of this made me feel tired, fed up and generally annoyed that the test code was harder to write than the actual production code! While the code did not feel right, the actual process was a by the book approach, so it must have been right. Taking a step back myself and my new pairing partner decided for a different approach. Lets abstract the controller internals we need and inject this into the controller. In turn our code would read better and the tests would be easy to construct.

Having made this refactoring the tests were still green. The refactoring was a great success. Now the test fixture set up consisted of a few simple lines. All the complex framework specific nonsense had disappeared. Getting to this stage took a bit of thought with regards the implementation, but we got there non the less. Having made this change, we wrote the next tests with such ease and joy it actually felt fun, enjoyable and completely stress free. Just how programming should be.

For the production code, as the framework is test friendly we had some unit tests around the concrete object used in production. For scenarios where this is not possible, a high level acceptance test to ensure things are wired up correctly would suffice. Either way we should always be confident when using code we do not own that it is correct, providing we use it correctly. After all, this will be heavily tested by the third party or so we hope. Manual testing will catch any integration issues with third party code with any luck.

The whole process was staggering, I was blown away by my ignorance. I knew the best practices, yet I chose to depend on concrete implementations rather than abstractions. After this session the whole theory behind mocking roles and not types [pdf] became so much clearer. This is yet one more revelation to add to the list. Every time I write Mock<name>, stop and think. Do I own the type? If not then maybe there is an abstraction waiting to escape, after all it will save a lot of pain.


Popular posts from this blog

Three Steps to Code Quality via TDD

Common complaints and problems that I've both encountered and hear other developers raise when it comes to the practice of Test Driven Development are: Impossible to refactor without all the tests breakingMinor changes require hours of changes to test codeTest setup is huge, slow to write and difficult to understandThe use of test doubles (mocks, stubs and fakes is confusing)Over the next three posts I will demonstrate three easy steps that can resolve the problems above. In turn this will allow developers to gain one of the benefits that TDD promises - the ability to refactor your code mercifully in order to improve code quality.StepsStop Making Everything PublicLimit the Amount of Dependencies you Use A Unit is Not Always a Method or ClassCode quality is a tricky subject and highly subjective, however if you follow the three guidelines above you should have the ability to radically change implementation details and therefore improve code quality when needed.

DRY vs DAMP in Tests

In the previous post I mentioned that duplication in tests is not always bad. Sometimes duplication becomes a problem. Tests can become large or virtually identically excluding a few lines. Changes to these tests can take a while and increase the maintenance overhead. At this point, DRY violations need to be resolved.SolutionsTest HelpersA common solution is to extract common functionality into setup methods or other helper utilities. While this will remove and reduce duplication this can make tests a bit harder to read as the test is now split amongst unrelated components. There is a limit to how useful such extractions can help as each test may need to do something slightly differently.DAMP - Descriptive and Meaningful PhrasesDescriptive and Meaningful Phrases is the alter ego of DRY. DAMP tests often use the builder pattern to construct the System Under Test. This allows calls to be chained in a fluent API style, similar to the Page Object Pattern. Internally the implementation wil…

Coding In the Real World

As a student when confronted with a problem, I would end up coding it and thinking - how do the professionals do this?For some reason I had the impression that once I entered the industry I would find enlightenment. Discovering the one true way to write high quality, professional code.It turns out that code in industry is not too far removed from the code I was writing back when I knew very little.Code in the real world can be:messy or cleanhard or easy to understandsimple or complexeasy or hard to changeor any combination of the aboveVery rarely will you be confronted with a problem that is difficult. Most challenges typically are formed around individuals and processes, rather than day to day coding. Years later I finally have the answer. Code in the real world is not that much different to code we were all writing when we first started out.If I could offer myself some advice back in those early days it would be to follow KISS, YAGNI and DRY religiously. The rest will fall into plac…

Feature Toggles

I'm a fan of regular releasing. My background and experience leads me to release as regularly as possible. There are numerous benefits to regular releases; limited risk, slicker release processes and the ability to change as requirements evolve.The problem with this concept is how can you release when features are not functionally complete?SolutionIf there is still work in progress, one solution to allow frequent releases is to use feature toggles. Feature toggles are simple conditional statements that are either enabled or disabled based on some condition.This simple example shows a feature toggle for an "Edit User" feature. If the boolean condition is false, then we only show the "New User" feature and the "Admin" feature. This boolean value will be provided by various means, usually a configuration file. This means at certain points we can change this value in order to demonstrate the "Edit User" functionality. Our demo environment could …

Reused Abstraction Principle

This is the second part of my series on abstractions.Part 1 - AbstractionsPart 3 - Dependency Elimination PrincipleThe Reused Abstraction Principle is a simple in concept in practice, but oddly rarely followed in typical enterprise development. I myself have been incredibly guilty of this in the past.Most code bases have a 1:1 mapping of interfaces to implementations. Usually this is the sign of TDD or automated testing being applied badly. The majority of these interfaces are wrong. 1:1 mappings between interfaces and implementations is a code smell.Such situations are usually the result of extracting an interface from an implementation, rather than having the client drive behaviour.These interfaces are also often bad abstractions, known as "leaky abstractions". As I've discussed previously, these abstractions tend to offer nothing more than simple indirection.ExampleApply the "rule of three". If there is only ever one implementation, then you don't need …