12

This has been driving me nuts lately...

What is refactoring?

Code refactoring is the process of restructuring existing computer code – changing the factoring – without changing its external behavior.

And how do we make sure we don't break anything during refactoring?

Before refactoring a section of code, a solid set of automatic unit tests is needed. The tests are used to demonstrate that the behavior of the module is correct before the refactoring.

Okay fine. But how do I proceed if I find a code smell in the unit tests themselves? Say, a test method that does too much? How do I make sure I don't break anything while refactoring the unit tests?

Do I need some kind of meta-tests? Is it unit tests all the way down?

Or do unit tests simply not obey the normal rules of refactoring?

TylerH
  • 20,799
  • 66
  • 75
  • 101
fredoverflow
  • 256,549
  • 94
  • 388
  • 662

4 Answers4

8

In my experience, there are two reasons to trust tests:

  • Review
  • You've seen it fail

Both of these are activities that happen when a test is written. If you keep tests immutable, you can keep trusting them.

Every time you modify a test, it becomes less trustworthy.

You can somewhat alleviate that problem by repeating the above process: review the changes to the tests, and temporarily change the System Under Test (SUT) so that you can see the tests fail as expected.

When modifying tests, keep the SUT unchanged. Tests and production code keep each other in check, so varying one while keeping the other locked is safest.

Mark Seemann
  • 225,310
  • 48
  • 427
  • 736
6

With respect this is an older post, it was referenced in a comment on my post about TDD in practice. So upon review, I'd like to throw in my two cents.

Mainly because I feel the accepted answer makes the slippery statement:

Every time you modify a test, it becomes less trustworthy.

I take issue with the word modify. In regards to refactoring such words like change, modify, etc are often avoided as they carry implications counter to refactoring.

If you modify a test in the traditional sense there is risk you introduced a change that made the test less trustworthy.

However, if you modify a test in the refactor sense then the test should be no less trustworthy.

This brings me back to the original question:

How do I refactor unit tests?

Quite simply, the same as you would any other code - in isolation.

So, if you want to refactor your tests, don't change the code, just change your tests.

Do I need test for my tests?

No. In fact, Kent Beck addresses this exact question in his Full Stack Radio interview, saying:

Your code is the test for your tests

Mark Seemann also notes this in his answer:

Tests and production code keep each other in check, so varying one while keeping the other locked is safest.

In the end, this is not so much about how to refactor tests as much as it is refactoring in general. The same principles apply, namely refactoring restructures code without changing its external behavior. If you don't change the external behavior, then no trust is lost.

Community
  • 1
  • 1
Jason McCreary
  • 71,546
  • 23
  • 135
  • 174
  • In addition to your nice answer, to my point of view the difference between refactoring and modification is that refactoring is about how you fulfill the requirements, while modification is more general and can change both **what** the requirements are, and **how** they are being accomplished. So a simpler form of what you stated to me is **"If you are sure that you aren't changing what your test is checking, then you are ready to go, but if you are in doubt, chaning the test (like removing or changing one of the assertions) is not safe."** – Mohsen Jan 02 '19 at 15:07
3

How do I make sure I don't break anything while refactoring the unit tests?

Keep the old tests as a reference.


To elaborate: unit tests with good coverage are worth their weight in results. You don't keep them for amazing program structure or lack of duplication; they're essentially a dataset of useful input/output pairs.

So when "refactoring" tests, it only really matters that the program tested with the new set shows the same behaviour. Every difference should be carefully, manually inspected, because new program bugs might have been found.

You might also accidentally reduce the coverage when refactoring. That's harder to find, and requires specialized coverage analysis tools.

Bartek Banachewicz
  • 38,596
  • 7
  • 91
  • 135
0

you don't know you won't break anything. to avoid the problem of 'who will test our tests?' you should keep tests as simple as possible to reduce the possibility of making an error.

when you refactor tests you can always use automatic refactoring or other 'trusted' methods like method extraction etc.

you also often use existing testing frameworks. they are tested by their creators. so when you start to build your own (even simple one) framework, complex helper methods etc, you can always test it

piotrek
  • 13,982
  • 13
  • 79
  • 165