For certain, the long term success of any project that leverages Tests has got to be Tests that are easy to understand and provide value.
For me, readability is the gateway to value. I want to be able to open the test, and BOOM! Here’s the subject, here’s the dependencies, this is what I’m doing, and here’s what I expect it to do. If I can’t figure it out within a few seconds, I start to question the value of the tests. And that’s exactly what happened to me earlier this week.
The test I was looking at had some issues, but the developer who wrote it had their heart in the right place and made attempts to keep it relatively straight-forward. It was using a Context-Specification style of test and was using Moq to Mock out the physical dependencies, but I got tied up in the mechanics of the test. I found that the trouble I was having was determining which Mocks were part of the test versus Mocks that were in the test to support related dependencies.
Below is an example of a similar test and the steps I took to clean it up. Along the way I found something interesting, and I hope you do, too.
Original (Cruft Flavor)
Here’s a sample of the test. For clarity sake, I only want to illustrate the initial setup of the test, so I’ve omitted the actual test part. Note, I’m using a flavor of context specification that I’ve blogged about before, if it seems like a strange syntax, you may want to read up.
using Moq; using Model.Services; using MyContextSpecFramework; using Microsoft.VisualStudio.TestTools.UnitTesting; public class TemplateResolverSpecs : ContextSpecFor<TemplateResolver> { protected Mock<IDataProvider> _mockDataProvider; protected Mock<IUserContext> _mockUserContext; protected IDataProvider _dataProvider; protected IUserContext _userContext; public override void Context() { _mockDataProvider = new Mock<IDataProvider>(); _mockUserContext = new Mock<IUserContext>(); _userContext = _mockUserContext.Object; _dataProvider = _mockDataProvider.Object; } public override TemplateResolver InitializeSubject() { return new TemplateResolver(_dataProvider,_userContext); } // public class SubContext : TemplateResolverSpecs // etc }
This is a fairly simple example and certainly those familiar with Moq’s syntax and general dependency injection patterns won’t have too much difficultly understanding what’s going on here. But you have to admit that while this is a trivial example there’s a lot of code here for what’s needed – and you had to read all of it.
The Rewrite
When I started to re-write this test, my motivation was for sub-classing the test fixture to create different contexts -- maybe I would want to create a context where I used Mocks, and another for real dependencies. I started to debate whether it would be wise to put the Mocks in a subclass or in the base when it occurred to me why the test was confusing in the first place: the Mocks are an implementation detail that are getting in the way of understanding the dependencies to the subject. The Mocks aren’t important at all – it’s the dependencies that matter!
So, here’s the same setup with the Mocks moved out of the way, only referenced in the initialization of the test’s Context.
using Moq; using Model.Services; using MyContextSpecFramework; using Microsoft.VisualStudio.TestTools.UnitTesting; public class TemplateResolverSpecs : ContextSpecFor<TemplateResolver> { protected IDataProvider DataProvider; protected IUserContext UserContext; public override void Context() { DataProvider = new Mock<IDataProvider>().Object; UserContext = new Mock<IUserContext>().Object; } public override TemplateResolver InitializeSubject() { return new TemplateResolver(DataProvider, UserContext); } }
Much better, don’t you think? Note, I’ve also removed the underscore and changed the case on my fields because they’re protected and that goes a long way to improve readability, too.
Where’d my Mock go?
So you’re probably thinking “that’s stupid great Bryan, but I was actually using those mocks” – and that’s a valid observation, but the best part is you don’t really need them anymore. Moq has a cool feature that let’s you obtain the Mock wrapper from the mocked-object anytime you want, so you only need the mock when it’s time to use it.
Simply use the static Get method on the Mock class to obtain a reference to your mock:
Mock.Get(DataProvider) .Setup( dataProvider => dataProvider.GetTemplate(It.IsAny<string>()) .Returns( new TemplateRecord() );
For contrast sake, here’s what the original would have looked like:
_mockDataProvider.Setup( dataProvider => dataProvider.GetTemplate(It.IsAny<string>()) .Returns( new TemplateRecord() );
They’re basically the same, but the difference is I don’t have to remember the name of the variable for the mock anymore. And as an added bonus, our Mock.Setup calls will all line up with the same indentation, regardless of the length of the dependencies’ variable name.
Conclusion
While the above is just an example of how tests can be trimmed down for added readability, my hope is that this readability influences developers to declare their dependencies up front rather than weighing themselves and their tests down with the mechanics of the tests. If you find yourself suddenly requiring a Mock in the middle of the test, or creating Mocks for items that aren’t immediate dependencies, it should serve as a red flag that you might not be testing the subject in isolation and you may want to step-back and re-think things a bit.
0 comments:
Post a Comment