Unit Testing – Is Refactoring for Unit Testing a Code Smell?

unit testing

Let me preface this question by saying that I get the need for unit testing. I painfully get it. You have to do it to ensure that future modifications don't adversely affect the application in ways you did not expect. It's also good practice to utilize a test driven methodology to ensure your current modifications work as expected while providing a solid testing base for adding edge cases (bugs) later. I also understand that testing is a requirement for any company that will, or plans to be, subject to an IT audit. And finally, I get that if your code is testable, it is probably written using solid design principles.

The quandary that I have is that coding to an interface for the purpose of supporting unit testing alone, seems like a contrived complexity.

Take the case where you have a database application that does not have the requirement to be database agnostic. You've already coded a DAL that communicates with that database and BL that uses that DAL for persistence. Integration tests are in place with the current architecture that utilize a development database for test data.

To support 100% code coverage on unit tests you would now need to introduce interfaces for both the DAL and the BL, have concrete implementations of those interfaces, and use dependency or constructor injection to utilize those interfaces based on the context of the runtime.

Does that not introduce a contrived complexity? A side question I guess is where is the line drawn between unit and integration testing in these cases? It's certainly feasible that the data model can change and you'd want integration testing in addition to unit testing because it's clear that unit testing will succeed where integration testing can fail in that case.

Best Answer

  1. 100% code coverage is an impractical goal - let's face facts here, to get that coverage, quite a few tests are going to be the sort to cover the code but not test it. Worse, you're spending a lot of time to make tests that are unlikely to cause error, or have minimal impact if errors occur.

  2. All database applications require database agnostic behavior - oh if I had a dollar for every time some said "oh, that isn't going to change". Sure, your database might not change, but what about licensing that code to 3rd parties? What about putting it on a mobile platform? What about putting it behind a web service? What about all the weird and interesting things we'll do in 5-10 years that you can't envision?

  3. (IMO) Don't change your design for testability alone - and now to the meat of your question and the more contentious point I will make: You are absolutely right that testable code is probably well designed code. If you are going to refactor your code, don't do it to make the code more testable, make it to be better designed. Unit tests serve as a great proxy for alternative needs. If it's hard to test, it will likely be hard to reuse. But sometimes you make a trade off that things will be hard to use in exchange for performance, or a nice interface, or to simplify code... Don't make your production code worse in favor of your test code. The tests are there to serve you after all.

But do be cautious - the common case is that hard to test code means hard to reuse code, and well done refactoring will help your test code and your production code.

Related Topic