29

Obviously some old applications can't be or is extremely difficult to unit test because of the way it was written in the first place.

But in places, like some helper methods which could probably be unit tested, should I bother writing unit tests for them?

I mean, they could be written like a dog, but however complexly fragile the logic, the test of time has proved that it works the way it should. Should I only bother to unit test if I'm say re-factoring it or should I write unit tests for them whenever I have the time?

Is there any value in doing this?

Take into account also that the method definition may be vague, and I may need to research what some of the methods should actually do in given conditions.

1

9 Answers 9

18

I think you should certainly write as many tests for the application as possible. They will help you learn the codebase and prepare you for the eventual refactoring or new development.

There are a few kinds of tests you can write in that scenario, each of them has their own merits. Writing these tests will teach you a great deal about the application you're dealing with.

First of all, before you set off writing tests for correctness, write tests that capture current behaviour, be it right or wrong. It's a pretty safe bet that you're going to uncover bugs in corner cases or in parts of the code that weren't thoroughly tested by running the program. Don't worry about what the code should do, just capture what it does. As you proceed, don't worry about reading the code or spending serious time figuring out what the output should be. Just run your test and capture that output in an assert.

That will give you a solid base of understanding of how the code operates and where the major pain points or weak areas may be. If you do uncover bugs, you can then approach people with the power to decide whether they're worth fixing or not and make those decisions.

Next, you can write a few bigger (in scope) tests that cover parts of the code that may not be easily unit-testable but where it would still be important to test workflows as much as possible. These workflow tests or integration tests, depending on how you want to look at them, will give you a good base for refactoring those workflows to make them more testable and protect you when a new feature needs to be added that might affect an existing workflow.

Over time, you will build up a suite of tests that is there to help you or the next person who ends up inheriting the application.

13

I see unit tests as specifications, not mere tests. In the specific case you have mentioned the real value will come when you have a unit test in place before making any change to conform to new specification. If you are going to change the inherited code then the best way I see is to have unit tests for the functionality, this will not only bring a safety net but also help us bring more clarity on what that particular unit does. It may also force you to do some research as mentioned by you which is good for the understanding. So my take is to get a module's unit test in place before it is about to undergo a change.

1
  • Write the tests in the highest level language that will work. When asked to estimate the time to make a change, include the time needed to write the missing tests. You may take longer to make the change, but you will put a lot fewer bugs in the field. Commented Jun 10, 2011 at 5:40
8
 > Is there any value in writing unit tests for code that 
 > already works when inheriting applications?

NO. From my experience there is a bad cost/benefit relation for writing unit tests after the production code was written because it is quite unlikely/difficult to find ways to test the code in isolation without heavy refactoring. One of the benefits of test driven development is that you get loosely coupled code. If you write the tests afterwards the chances are high that the code is tightly coupled.

YES you could write Integration tests for the application for those functions where you plan to make changes to. This way you can have regression-test to see if your changes break something.

YES from the quality and developer point of view it is valuable to have Tests.

NO from the business point of view because it is very hard to sell the customer that you need time/money for getting no real business value but the vague promise that business-changes will not make things worse.

1
  • +1 for integration tests rather than unit test for this case. Very pragmatic advice
    – gbjbaanb
    Commented Jun 22, 2011 at 10:17
5

If at all possible there is even more reason to write tests for existing code that "already works". If there are no tests then chances are the code is ripe with smells and could use extensive refactoring to be improved; the test suite will help you to refactor. It might even reveal that the original code does not work properly; I recall once I wrote a test for a method generating a sales report and I found it wasn't calculating things properly so sales were a few thousand dollars more than they really were, and nobody had known in the five years that code was in production (luckily it was not the actual financial end of things, just a sales report estimate).

Of course, this advice only applies if you actually can write the tests. In my experience, a codebase without tests is almost impossible to retrofit tests for because you would have to rewrite half of the code modules to be abstract enough to support testing first, and you won't be able to do that.

1
  • Why a downvote for stating the truth? Commented Jun 10, 2011 at 14:57
4

Absolutely yes, because unit tests should not verify correctness but characterize the actual behavior of the system. Especially with a legacy project, many of the requirements are implicitly encoded in the actual behavior.

This characterization serves as a baseline of functionality. Even if the behavior is incorrect from a business standpoint, you preclude the behavior degrading farther. Gaining traction in a legacy project can be difficult, and this gives you a baseline so that you can move forward without making things worse.

So, write all the unit tests you can. For code that can't be tested, refactor your code to separate dependencies by introducing "seams" into the code -- locations where you can separate out the code you want to test from the code you don't want to test.

The ideas of unit tests as characterization tests and seams are main points in Working Effectively with Legacy Code by Michael Feathers, a book which I highly recommend.

2

I’ve pondered about this myself as there is quite a lot of undocumented seamingly badly written, but working legacy code in our system.

You make a fine point:

Should I only bother to unit test if I'm say re-factoring it or should I write unit tests for them whenever I have the time?

If the requirements change I think it would be most valuable to write the units tests. If not I wouldn’t bother especially since you wouldn’t be able to write proper tests, because you don’t know all of the conditions.

whenever I have the time?

You have time when it gets prioritized, right?, so it will never happen anyway. :)

1

Are you trying to leverage aspects of that codebase which have not previously been used in a production application? Write unit tests for those scenarios first. You need to be able to prioritise your work here, and I think The Art of Unit Testing had some advice in that regard (in particular how to approach testing legacy codebases).

1

Don't assume that everything's working just because the application is up and running ... typically, the "test of time" only shows that a) you haven't found the rest of the defects yet or b) the people who have found them haven't reported them. (Or perhaps both.) And even the code that's presumably working now might not work the next time you have to make a change.

Unit tests give you a measure of certainty that certain functionality remains intact, while also providing you with a way to demonstrate this for a reasonable quantity of cases. That's significantly more valuable than "poke around and test this", even for a single bug fix. (Poking around is more like the equivalent of integration or system testing, so in that sense, manually testing the app can cover more than unit tests alone can, but that's still time spent in an inefficient manner. Even some method of automating those tests is better than doing them manually.)

You should definitely write tests when you change code, whether it's fixing or refactoring. You might do well to write tests as you can, if for no other reason than to save some time the next time you have to fix a defect (and there will be a next time) ... although in this case, you'll have to balance that time against the expectations of people who pretend that software is always defect-free and thus time spent toward future maintenance is "wasted". (That doesn't mean you should ignore current schedules, though.) At some point, if you do this, you'll have enough coverage that you can begin to demonstrate the value of testing on legacy applications, and that might help you spend that time on future apps, which should eventually free up time to spend on productive development.

0

From my lala-land heart, I will say yes and not only that, but hack it, refactor it and keep testing, but make sure you don't get behind in other important projects. As a working programmer, If your job is at risk or if a manager told you to take ownership, then yes. If the team or the company might suffer, negotiate with your manager to let him know that is necessary to do testing to keep the company from disaster. If he says don't worry, then you are out of the hook (not necessarily, blame delegation is a management skill, so make sure you are out of the hook...really!)

Good luck!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.