23
  • I'm talking about unit tests in the TDD sense. (Not automated "integration", or what you like to call it tests.)
  • Legacy code as in: (C++) code without tests. (see: Michael Feathers' Working Effectively with Legacy Code)
  • But also legacy code as in: Code that our team has been working with for the last 10-5 years, so we very often have quite a good idea of where to put things to change something.
  • We do have unit tests in place (via Boost.Test) for some modules that came later or have been a "natural" fit for unit tests (common app specific containers, string-stuff, network helpers, etc.)
  • We do not yet have proper automated acceptance tests.

Now, recently I had the "pleasure" to implement 3 new user-facing features.

Each of those took me about 1-2 hours of getting up to speed with the code parts I needed to change, 1-2h hours to implement the (little) code I needed to change and another 1-2 hours to make sure the app ran correctly afterwards and did was it was supposed to do.

Now, I really added little code. (I think one method and a few call lines for each feature.)

Factoring out this code (via any of the methods suggested in WEwLC), so that a unit test would've make sense (and not been a complete tautology) would have easily taken another 2-4 hours, if not more. This would have added 50%-100% time to each feature, with no immediate benefit, as

  1. I did not need the unit test to understand anything about the code
  2. Manual testing is the same amount of work, as I still need to test if the code is correctly integrated into the rest of the app.

Granted, if, later on, "someone" came along and touched that code, he theoretically could have some benefit from that unit test. (Only theoretically, as that tested island of code would live in a ocean of untested code.)

So, "this time" I chose to not do the hard work of adding a unit test: The code changes to get that stuff under test would have been significantly more complex than the code changes to get the feature correctly (and cleanly) implemented.

Is this something typical for strongly coupled legacy code? Am I lazy / do we set the wrong priorities as a team? Or am I prudent, only testing stuff where the overhead isn't too high?

Martin Ba
  • 7,862

7 Answers7

20

You have to be pragmatic about these situations. Everything has to have a business value, but the business has to trust you to judge what the value of technical work is. Yes, there is always a benefit to having unit tests, but is that benefit great enough to justify the time spent?

I would argue always on new code but, on legacy code, you have to make a judgement call.

Are you in this area of code often? Then there's a case for continual improvement. Are you making a significant change? Then there's a case that it is already new code. But if you're making a one-line code in a complex area that will probably not be touched again for a year, of course the cost (not to mention risk) of reengineering is too great. Just slap your one line of code in there and go take a shower quick.

Rule of thumb: Always think to yourself, "Do I believe that the business benefits more from this technical work that I feel I should do than the job they asked for which is going to be delayed as a result?"

pdr
  • 53,768
10

The gain of unit tests in TDD sense is to get something that stands by itself, without needing oral tradition built over years by a stable team.

It is a big luck that the same team has been on the same project for so many time. But this will eventually change. People get sick, bored or promoted. They move, retire or die. New ones come with new ideas and the urge to refactor everything the way they learned at school. Companies get sold and bought. Management policies change.

If you want that your project survive, you have to prepare it for changes.

mouviciel
  • 15,491
4

Writing unit tests is future proofing your code. If the code base does not have tests, then you should add new tests for all new features because it makes that bit of the code just that little more robust.

I find that adding tests, even in legacy code, is beneficial in the medium to long run. If your company does not care about the medium to long term, I would look for another company. In additions, I do not think that it takes longer to test manually than it take to write a set unit test. If it does, then you need to practice code kata focused on writing tests.

2

Certainly best practices dictate you should unit test any code you change. Real world coding however involves a lot of compromises, time and materials are finite.

What you need to do is evaluate the cost in real terms of fixing bugs and making modifications without unit tests. You then can asses against the investment in creating those tests. Unit tests greatly reduce the cost of future work but come at a price now.

Bottom line is if your system is rarely changed then it may not be worth writing unit tests but if it is subject to regular changes then it will be worth it.

Tom Squires
  • 17,835
1

All of these little rational decisions to not create tests are building up a crippling technical debt that will come back to haunt you when somebody leaves & a new hire has to come in & get up to speed.

Yes, it might have cost another 2-3 hours to write the unit tests but if the code gets sent back from testing you'll have to manually test everything again, and document your testing, again - you do that don't you? Otherwise how does anyone know what you tested and what values you used?

Unit tests document the expected behaviour of the code the test data used to check the functionality and gives new coders reasonable confidence that they haven't broken something when making changes.

It costs a couple of hours today more than manual testing but then you are testing every time you do any change saving hours over the life of the code.

However, if you know that the code will be retired in a couple of years all I've said changes because you won't finish putting tests in the code & it'll never pay back.

mcottle
  • 6,152
  • 2
  • 26
  • 27
0

The value of unit tests does not only lie in testing a new feature when that is developed. The benefit of unit tests is gained mostly in the future.

Yes, you may have to spend what seems like an inordinate amount of time to make your legacy code testable.

But if you don't, you will, or certainly should, spend many, many, many more hours testing the feature. Not just on its initial development, but with every new version of your software. Without unit tests and other automated testing, you do not have any other option but to spend many hours of manual testing to ensure that your feature was not inadvertently broken, even when that feature itself was not changed.

0

Each of those took me about 1-2 hours of getting up to speed with the code parts I needed to change, 1-2h hours to implement the (little) code I needed to change and another 1-2 hours to make sure the app ran correctly afterwards and did what it was supposed to do.

So you tested that the changed code did what you expected it to do.

Did you also check for:

  • All the things that make it fail, or
  • Things that you might not have expected it to do?

Tests do not prove the correctness of your code today. You do that by writing the code to do what it needs to do and that's why it feels like "wasted" effort to do the job "twice"; once to write the code and again to write the tests.

Tests can prove the continued correctness of your code over time. You (or somebody else) will make changes to the code over the months and years that follow, that's just the Nature of software, and your tests will ensure that you (or they) don't break anything that you didn't intend to.

Manual testing is the same amount of work, as I still need to test if the code is correctly integrated into the rest of the app.

Once the tests are written - and tested, obviously :-) - then the amount of "work" they take reduces to practically Zero. You just run the tests. Any amount of manual testing will almost certainly be more than this.

Phill W.
  • 13,093