CookiesHQDevelopmentWeb

Dealing with legacy code, part II, the tests

By February 20, 2015 No Comments

This is the part II or our series of blog posts focussing on how we deal with legacy code when taking over a project.

In part I, I explained our discovery phase process when we take over an existing project.

In Part II, I wanted to touch on the test subject.

We write tests to help future developers understand the logic of the application (and not the logic of the code ;)) but also, sometimes, to serve as documentation.

Usually it’s all gravy when you are dealing with your very own projects, but when you take over someone else’s project, this can become problematic.

By experience, you can identify four scenarios:

  • Code with good test coverage, using your ideal test framework.
  • Code without any test coverage.
  • Code with good test coverage, but using a framework you’re not used to.
  • Code with bad test coverage.

Let’s see how we can deal with each one.

Code with good test coverage, using your ideal test framework

Obviously this is the best scenario. The previous team was using the same stack as you do, and followed the same conventions.

It will make the introduction of new features easy as you can rely on tests to ensure that your changes do not introduce any regressions.

Plus, as the previous developer uses your framework of choice (Rspec for us ;)) it will be straightforward to extend the existing test suite.

As a tip, I would take a bit of time to understand the writing style of the previous developer(s). Try to match it in your future tests and this will make the project more consistent in the end.

Code without any test coverage

As bad as its sounds, I would not consider this as the worst scenario.

Obviously it does depends on the size of the application, but by following some simple tips you can make it manageable.

First, as we’ve seen for Part I, you should integrate an exception tracker. Then, deploy to a staging environment and ask your Q&A person to pass through all the scenarios (s)he can think of in the application.

If there are any bugs they will be reported by the exception tracker, and this will give you a nice first list of tests to write and bugs to fix.

Ideally you would want to have your exception tracker on the production application before you’ve made any changes in the code. It also lets the website users report bugs as well.

The second thing to do is to write generic but important integration tests. Think of thing such as user creation / user login, element navigations and so on. They don’t have to be extensive yet, but they will serve as a base to make sure you don’t break important parts of the app.

Then it’s time to tackle what the exception tracker found.

This is nice because, ideally, you will be able to write large tests, testing multiple things at once for now, but one element in the middle is expected to break. Then you can fix it in the code and ensure this won’t happen again and get the bonus of having tested multiple parts of the application.

Lastly, when you find heavy pieces of logic in your discovery phase, you can use large generic integration tests again, ensuring that the whole end to end logic works.

For example, say consider this scenario:

When I buy a product, the product should be removed from the available stock, an invoice should be created with the right values, an email should be fired to the buyer and an email should be fired to the stock warehouse.

Ideally you would want each element to be tested independently. But when dealing with legacy code, you have to move fast.

I like to have a big integration test that might test all those interactions as once, as this will ensure at least that some code logic is tested. Then in the future, when you have to come back to this part of the application, you can start breaking things a bit.

Code with good test coverage, but using a framework you’re not used to

This one is the trickiest for me. On one hand your test coverage is good and your tests are useful, but on the other hand it’s using a test framework that you dislike.

In this scenario, we always resist the urge of adding a new test framework to the pile. It’s very tempting, but will create a lot of duplicate tests in the end, and will eventually slow down your test suite.

You will have to learn the new framework and deal with it for the time being, sorry”.

Code with bad test coverage

This is the worst scenario. Having a bad test coverage will introduce a false sense of security that will bite you back in the future.

In this scenario, I would prepend all the existing tests with “(legacy)” so you quickly know which one is which when writing new tests. You will also be able to decide quickly which one you want to delete and the ones you will want to keep. Then you can treat your project as having no tests at all, and rely on your integration tests to make sure you catch important scenarios.

In a nutshell

Dealing with legacy tests is usually one of the fun parts of a project that has legacy code.

But if you follow some simple rules, you should hopefully end up with a strong suite in no time.

Part III of “Dealing with legacy code” will concentrate on the project management changes that occur when clients move from one company to another. ”