The first time I was exposed to mocking was in the early days of .NET when a good friend of mine and I took a class in New York City. I was already a fan of unit testing, and was just starting down the path of Test Driven Development. The class was one of those boot camp style classes – five 12-hour days, more typing than thinking, rapid-fire concepts and code. Although the class itself was not a great experience, we did a lot of mocking in our unit tests. It was my first real exposure to mocking, and I really saw the value of mocking. However, since the class was so rapid fire typing just trying to keep up with the instructor, I did not have a chance to understand the way mocking works.
I got back to my desk in Cincinnati the following Monday, downloaded RhinoMocks (pretty much the only mocking framework for .NET at the time), rubbed my hands together, and said to myself “I’m ready to mock something”. Then I stared at my code base and my unit tests and realized that I did not have a clue what to do.
Since then, I have come a long way, and if you have followed my blogs at all, you know that mocking is a vital tool for me when I code. My tool choice has changed (I use JustMock these days), and the way I write code has evolved to focus on Behavior Drive Development/Design with Test Driven Development augmenting where it makes sense.
It was a journey, and not a sudden transformation. In the beginning, I was developing TED (Test Eventual Development) style, I was not a good practitioner of SOLID, and a host of other things that I look back on and think ick – did I really write that?
How did I get to where I am now? I learned the best uses for mocking from refactoring the code that I was currently developing, and it taught some very valuable lessons in addition to how to mock – lessons that I use to this day, even when I am developing using BDD or TDD practices. Unfortunately, I do not have a secret recipe or a magic pill for you. Everyone’s journey from developer to artisan is a journey that is as unique as we are as individuals. However, I can share what I have learned along the way, and hopefully something strikes a chord and illuminates the way to make your path a little clearer.
The first thing I learned is that you cannot force it. This is true with most aspects of software development, but is especially true with mocking. I had found a really cool tool, and it became my hammer. I spent all of my time trying to turn everything I could find into a nail. After a day of this, I ended up with a bunch of holes from the hammer strikes, and I was definitely worse off than when I started. I thought, “Maybe mocking wasn’t so cool after all…”
After that (rather frustrating) day of playing whack-a-mole with my code, I stopped and took a step back. Obviously, mocking is not some magic bullet that will solve all of my development problems and prevent the zombie apocalypse. After all, I had been writing software since the mid 1980’s, and did not have a mocking framework before. So I took a day off from billing, and decided to start a spike (we did not call it a spike back then, but you know what I mean) to learn the proper way to mock.
What was the real problem that I was trying to solve? I was on this path of learning to write unit tests before writing the line of business code. I had been writing “unit” tests before I learned about mocking, but honestly, they were far from testing units of code. Many of my tests were executing multiple methods, or entire slices of code, due to the dependencies between different code constructs. Yes, I had good code coverage, and the tests definitely alerted me to side effects or other issues that tend to end up as production bugs. However, I was still stepping through a significant amount of code in the debugger when there was an issue because I did not have good code Isolation.
About this time, I heard a quote that really resonated, and I use it to this day when I teach software development.
“If you are newing and using in the same method, you’re doing it wrong.”
Ok, so “newing” really is not a word, but simply stated, if you are instantiating an object, and then using it, your code is so tightly coupled that you are locked into a single execution path. In addition to many of problems this can lead to (lack of extensibility, fragility of code, etc.), this is a showstopper when trying to write unit tests.
With this in mind, I started looking at my code in a completely different light. Any method that required an external object became a candidate for dependence injection. I say a candidate, because (as with anything in software development) every change needs critical thought applied.
So I went about creating interfaces for my dependent objects, and passing them in with Dependency Injection. The next thing I noticed is that I had methods with a lot of dependencies. Yes, I could now use mocks to test them, but creating a whole cadre of mocks just to test one method felt bad. And it typically is. Extracting the dependencies cleared my sinuses and showed me code smells that I did not spot before. I was doing too much in my methods, breaking Single Responsibility and Separation of Concerns. The good news is that refactoring to use Dependency Injection illuminated this problem - never mind that some of my methods were several screens in length!
I knew I had to do something about these overly complex methods, but I knew that heavy changes typically lead to bugs. So I sucked it up, and wrote my unit tests – at least now they were truly unit tests. In addition, I had a valid case for mocking as I could now isolate all of those dependencies that I just abstracted away with dependency injection. Back then, I did not have access to automocking, which would have made this part of the process a lot easier, but I did not, so I carefully created all of my mocks individually.
I had to make a conscious decision at this point. I knew that if I were to correct my cyclomatic complexity issues in my code, I had some heavy refactoring to do, which meant that most of my unit tests would become throw away. I also knew that side effect bugs happen most often when refactoring code, and are the hardest to detect in the development phase. So mocking it was. I spent a significant amount time writing unit tests for the code that I was about to change.
As developers, we are very keen on scope changes with the project. We are usually the first ones to cry “FOUL” when the project manager or the business tries to sneak in some additional work. When it comes to our own effort management, we are not always as judicious. My first pass in refactoring saw me trying to redo the entire project at once. I hated life for quite some time.
Look at smaller sections of code, starting with code that is either a) volatile already or b) impacted by a requirement or a bug. Unlike mortgage debt, technical debt does not always have to be paid back, so making changes just for making changes might not provide business value.
Many communities are passing laws that prevent driving and texting, because studies have shown that doing too many things at once means you do not do any of them particularly well. Your code should do one thing at a time. A very big sign that you are doing too much in code is the number of dependencies that your method needs. Another sign is the length of your method – if your method is longer than one screen (at a normal font – no cheating allowed!) it is probably doing too much.
With my tests in place, and plenty of mocks to handle the dependencies, I started extracting small units of work out of my monster methods. My existing tests were still valuable, since they encapsulated the extracted methods in the original parent method calls (although academically they were now probably more like integration tests than unit tests), and helped me prevent introducing bugs as I cleaned up my code.
As I stripped my methods down into the smallest units of work, I discovered three main benefits. 1) I found a lot of repeated logic that allowed me to remove code, 2) my code was becoming more readable, and 3) unit tests were significantly easier to write.
I have written a lot about how to mock over the course of my blogging career, but I think it is important to take a step back and answer the “Why should I mock” and “What does mocking really do for me” questions. My experiences in cleaning up a legacy code base taught me many things, but first and foremost, that mocking matters. Could I have cleaned up my code without using mocks? Certainly. I could have used test doubles in my tests, or just not tested anything. Both the value of unit tests and the friction of using test doubles have been well documented, and I knew those options weren’t going to move my code base forward and deliver value for the customer.
By introducing mocks into my unit tests when they were needed, I could refine and refactor my code to make it cleaner, eliminate duplications, increase code coverage, and make the code easier to maintain, reducing the total cost of ownership.
After that experience, I had my “aha” moment in writing tests. Test Driven Development (and later Behavior Driven Development) became a whole lot clearer. By applying SOLID principles to both my unit tests and my line of business code, I was writing cleaner, clearer, more maintainable code from the start. Mocks allowed me to abstract away the dependencies, and if I spent too much time creating and arranging mocks in my unit tests, I realized that was a code smell, and looked into making my units of work smaller and more refined. Mocking really does matter!
Philip Japikse is an international speaker, a Microsoft MVP, ASPInsider, INETA Community Champion, MCSD, CSM/ CSP, and a passionate member of the developer community. Phil has been working with .Net since the first betas, developing software for over 20 years, and heavily involved in the agile community since 2005. Phil also hosts the Hallway Conversations podcast (www.hallwayconversations.com) and serves as the Lead Director for the Cincinnati .Net User’s Group (http://www.cinnug.org). You can follow Phil on twitter via www.twitter.com/skimedic, or read his personal blog at www.skimedic.com/blog.
Copyright © 2017, Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
Progress, Telerik, and certain product names used herein are trademarks or registered trademarks of Progress Software Corporation and/or one of its subsidiaries or affiliates in the U.S. and/or other countries. See Trademarks or appropriate markings.