Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I ensure that I don't break the test code when I refactor it?

Code evolves, and as it does, it also decays if not pruned, a bit like a garden in that respect. Pruning mean refactoring to make it fulfill its evolving purpose.

Refactoring is much safer if we have a good unit test coverage. Test-driven development forces us to write the test code first, before the production code. Hence, we can't test the implementation, because there isn't any. This makes it much easier to refactor the production code.

The TDD cycle is something like this: write a test, test fails, write production code until the test succeeds, refactor the code.

But from what I've seen, people refactor the production code, but not the test code. As test code decays, the production code will go stale and then everything goes downhill. Therefore, I think it is necessary to refactor test code.

Here's the problem: How do you ensure that you don't break the test code when you refactor it?

(I've done one approach, https://thecomsci.wordpress.com/2011/12/19/double-dabble/, but I think there might be a better way.)

Apparently there's a book, http://www.amazon.com/dp/0131495054, which I haven't read yet.

There's also a Wiki page about this, http://c2.com/cgi/wiki?RefactoringTestCode, which doesn't have a solution.

like image 634
Roger C S Wernersson Avatar asked Dec 19 '11 21:12

Roger C S Wernersson


People also ask

What is the best way to ensure that code fix doesn't break existing functionality?

Refactoring your code makes it easier to read and maintain. Refactoring doesn't change the external functionality of your code; it changes the way the code achieves that functionality.

How do you make a unit test resistant to refactoring?

The only way to avoid brittleness in tests and increase their resistance to refactoring is to decouple them from the SUT's implementation details—keep as much distance as possible between the test and the code's inner workings, and instead aim at verifying the end result.

How would you ensure the new commits won't break the existing system?

Don't Break Builds Another version control best practice is to avoid breaking builds by doing complete commits. Provide test cases and at least stubs for new APIs. This ensures every commit is usable by any other member in the team without breaking their build. A complete commit is easier to propagate between branches.


Video Answer


2 Answers

Refactoring your tests is a two step process. Simply stated: First you must use your application under test to ensure that the tests pass while refactoring. Then, after your refactored tests are green, you must ensure that they will fail. However to do this properly requires some specific steps.

In order to properly test your refactored tests, you must change the application under test to cause the test to fail. Only that test condition should fail. That way you can ensure that the test is failing properly in addition to passing. You should strive for a single test failure, but that will not be possible in some cases (i.e. not unit tests). However if you are refactoring correctly there will be a single failure in the refactored tests, and the other failures will exist in tests not related to the current refactoring. Understanding your codebase is required to properly identify cascading failures of this type and failures of this type only apply to tests other than unit tests.

like image 125
Charles Lambert Avatar answered Sep 20 '22 19:09

Charles Lambert


I think you should not change your test code.

Why? In TDD, you define a interface for a class. This interface contains methods that are defined with a certain set of functionality.The requirements / design.

First: These requirements do not change while refactoring your production code. Refactoring means: changing/cleaning the code without changing the functionality.
Second: The test checks a certain set of functionality, this set stays the same.

Conclusion: Refactoring test and refactoring your production code are two different things.

Tip:When write your tests, write clean code. Make small tests. Which really test one piece of the functionality.

But "Your design changes because of unforeseen changes to the requirements". This may lead or may not lead to changes in the interface.
When your requirements change, your tests must change. This is not avoidable.
You have to keep in mind that this is a new TDD cycle. First test the new functionality and remove the old functionality tests. Then implement the new design.

To make this work properly, you need clean and small tests. Example:

MethodOne does: changeA and changeB

Don't put this in 1 unit test, but make a test class with 2 unit tests.
Both execute MethodOne, but they check for other results (changeA, changeB).

When the specification of changeA changes, you only need to rewrite 1 unit method.
When MethodOne gets a new specification changeC: Add a unit test.

With the above example your tests will be more agile and easier to change.

Summary:

  • Dont refactor your tests, when refactoring your production code.
  • Write clean and agile tests.

Hopes this helps. Good luck with it.

@disclaimer: I do not want your money if this makes you rich.

like image 35
Mats Stijlaart Avatar answered Sep 20 '22 19:09

Mats Stijlaart