Testing Changes to Untested Code
(archived from my old blog from my pre-MS days)
Let's face it, not everyone has automated unit tests for their code. You may, however, have inherited some code and are responsible for making changes to that code or for creating additional functionality. How do you handle such a situation?
The best way is to find places in the code into which you can place a pry-bar (not a technical term) and wedge a couple of components apart. This is the best way, but it's also messy.
Pry-Bar Method
I believe this comes from Working Effectively with Legacy Code (Robert C. Martin Series), by Michael Feathers, though I haven't read the book personally (I need to get myself a copy). But I do understand the concept.
Let's say that you have a class Engine, which contains a class Carburetor. Let's also say that you have to modify some functionality of Engine. What you need to do is isolate Engine so that it can be tested all by itself. We don't care about the Carburetor class at this time - it's to be tested elsewhere (eventually). Here are the appropriate steps to "pry away" Engine's dependency on the Carburetor class:
- If it doesn't already exist, create an interface for Carburetor's functionality, ICarburetor, such that ICarburetor contains at least all the methods/properties that are called by the Engine class. Refactoring tools like JetBrains ReSharper make this very easy.
- Modify Engine's constructor so that it accepts an ICarburetor parameter and set a private reference to an ICarburetor object(there are variations on this theme, but this is my favorite).
- In your test, create a mock object of the ICarburetor type, either "manually" or by using a mock framework like Rhino Mocks.
- Set up any expectations of the mock object if required (that's a future post, or just Google Rhino Mocks).
- Pass your ICarburetor mock object into your Engine constructor.
- Do your test on Engine.
- Confirm your expected state (and/or interactions with the mock object)
I hear what you're saying, "That's an awful lot of work for testing some code that's not been tested before!" Yep. However, if you ever intend to get the whole system under proper testing, then you've got to do it that way. What makes it more painful is when you don't have only one "dependency" (ICarburetor) but perhaps dozens (Carburetor, shocks, exhaust, fuel tank, oil, transmission, etc.). Whew!!!
Sometimes you may only be responsible for making a change to code ASAP and then go away. In this case, you're not likely to lose sleep over the fact that someone has written some untested code (gasp!). All you're responsible for is a small tweak. What do you do in this situation?
Lazy Method (perhaps too lazy)
For some reason, I find myself in this situation more often than not, perhaps since as consultants we're not often called in after the fact for long-term code maintenance. You certainly could use the Pry-Bar method above, but it may involve creating a lot of "interface noise" (at least the client sees it that way) when all the client wanted was the web-page widget to appear blue instead of red (ok, that's a little extreme, but you get my point). So what do we do? In this case, I'll sometimes resort to the "lazy" method. In this case, I really do mean lazy. In agile terminology, being lazy is not always a bad thing. However, in this case "lazy" may not be such a good thing, but hey - you do what you can to make people happy.
So here you simply create a class (these classes tend to "want to be" singletons, but don't have to be) that take data from the "untested" code and return data to the untested code (or don't in the case of "void"). You can test the "lazy class" all you want this way. Again, not the best way, but perhaps the quickest for short-fuse work.
Whichever option you choose, it's better than not testing your code at all, which is probably why they called you in for help in the first place.