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.
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.
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.
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.
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.
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:
Hopes this helps. Good luck with it.
@disclaimer: I do not want your money if this makes you rich.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With