24

I currently have like 10 tests that test whenever my Tetris piece doesn't move left if there is a piece in the path, or a wall. Now, I will have to test the same behaviour for the right movement.

Is it too bad if I just copy the 10 tests I already have for the left movement and make only the needed changes and do the same for the code itself too? Or should I go again and make each test from the beginning, even so if the logic is basically the same?

Rob
  • 45,296
  • 24
  • 122
  • 150
devoured elysium
  • 101,373
  • 131
  • 340
  • 557

9 Answers9

36

I have a somewhat controversial position on this one. While code duplication must be avoided as much as possible in production code, this is not so bad for test code. Production and test code differ in nature and intent:

  • Production code can afford some complexity so as to be understandable/maintainable. You want the code to be at the right abstraction level, and the design to be consistent. This is ok because you have tests for it and you can make sure it works. Code duplication in production code wouldn't be a problem if you had really a 100% code coverage at the logical level. This is really hard to achieve, so the rule is: avoid duplication and maximize code coverage.

  • Test code on the other hand must be as simple as possible. You must make sure that test code actually tests what it should. If tests are complicated, you might end up with bug in the tests or the wrong tests -- and you don't have tests for the tests, so the rule is: keep it simple. If test code is duplicated, this is not so a big problem when it something changes. If the change is applied only in one test, the other will fail until you fix it.

The main point I want to make is that production and test code have a different nature. Then it's always a matter of common sense, and I'm not saying you should not factor test code, etc. If you can factor something in test code and you're sure it's ok, then do it. But for test code, I would favor simplicity over elegance, while for production code, I would favor elegance over simplicity. The optimum is of course to have a simple, elegant solution :)

PS: If you really don't agree, please leave a comment.

Destroyica
  • 4,147
  • 3
  • 33
  • 50
ewernli
  • 38,045
  • 5
  • 92
  • 123
  • 1
    +1, I find ur argument compelling. Maybe we could say if ur code has x level complexity, then it needs to be tested. This applies to testing code too. So if a proper refactoring raises the testing code complexity above x, then either (1) refactor and add another testing level; or (2) Keep It Simple Stupid. – emory Aug 08 '10 at 21:52
  • 1
    I agree with your point that test code should be simple. But, then at the same time it should be maintainable and duplicating code isn't what you would like to do – P.K Aug 10 '10 at 04:46
  • 4
    @P.K Actually, code with duplication is most of the time easier to understand, because it's complexity is low (less abstraction). So maintenance isn't hard, but might be a bit repetitive. In software engineering, most of the time is spend *understanding* what needs to be done, not *doing* it. If maintaining tests is repetitive and boring, that's fine, that the way it should be. The problem with duplication is the fear to not update the logic in all places. You can not take the risk for production code to leave an incoherence unnoticed, but in test code, the test will fail until you change it. – ewernli Aug 11 '10 at 05:16
  • Keep test code simple as possible. It should be readable by a novice. Avoid abstraction because it increases risk of a non obvious failure . Using xUnit patterns IUseFixture or nUnit shared setup and takedown can avoid much copying and pasting and boiler plate test code without confusing matters. – Dave Dec 06 '12 at 11:12
  • 1
    test code and production code may have a different nature, and I agree test code should attempt to keeps it's approach simple, but I don't agree that code duplication should be advocated. It should be avoided when possible. If your test project has hundreds of test cases (integration level) the effort needed to maintain those cases can quickly become unwieldy. Leaving your product exposed to 'issue creep' while cases are failing due to necessary test infrastructure maintenance and development changes are pouring in is not an ideal situation. – Muad'Dib Jul 26 '18 at 13:02
  • Limiting the duplication of signatures as much as possible, reduces the ripple effects due to changing the signature of production code. And thats why I apply the xunit's creational patterns. – Mohsen Jan 05 '19 at 08:33
34

Try taking the 3rd approach that you haven't mentioned, that of refactoring your code so that you can share one implementation of the test between all 10 tests.

The jist is, duplicating code is almost always the wrong thing to do. In this example you could refactor the checking code into a method called, for example IsTetrisPieceUnableToMoveLeftBecauseOfAPieceOrAWall. I always go for very descriptive method names like that when writing a bit of "shared" functionality for a unit test as it makes it extraordinarily clear just what's being done / tested.

Rob
  • 45,296
  • 24
  • 122
  • 150
14

Test code is like any other code and should be maintained and refactored.

This means that if you have shared logic, extract it to its own function.

Some unit test libraries such as the xUnit family have specific test fixture, setup and teardown attributes for such shared code.

See this related question - "Why is copy paste of code dangerous?".

Community
  • 1
  • 1
Oded
  • 489,969
  • 99
  • 883
  • 1,009
10

There's nothing wrong with copy-pasting, and it's a good place to start. Indeed, it's better than from scratch, as if you've got working code (whether tests or otherwise), then copy-paste is more reliable than from scratch, as well as quicker.

However, that's only step 1. Step 2 is refactoring for commonality, and step 1 is only to help you see that commonality. If you can already see it clearly without copying (sometimes it's easier to copy first and then examine, sometimes it isn't, and it depends on the person doing it) then skip step 1.

Jon Hanna
  • 110,372
  • 10
  • 146
  • 251
2

If you are repeating code, then you must refactor. Your situation is a common problem and is solved using 'Parameteric Testing'. Parameteric testing when supported by the test harness allows for passing multiple sets of input values as parameters. You may also want to look up Fuzz testing, I have found that it useful in situations such as this.

keios
  • 462
  • 4
  • 10
2

The xunitpatterns.org web site says "no" (copy / paste is not ok) because it can increase costs when tests need to be updated:

"Cut and Paste" is a powerful tool for writing code fast but it results in many copies of the same code each of which must be maintained in parallel.

and for further reading it also links to the article

By: Arie van Deursen, Leon Moonen, Alex van den Bergh, Gerard Kok

mjn
  • 36,362
  • 28
  • 176
  • 378
2

I agreed @Rob. The code needs refactoring. But if you don't want to do refactor the code at this point of time then you can go and have parametrized tests. The same test run for different parameters. See TestCase and TestCaseSource attributes in nunit.

Refer http://nunit.org/index.php?p=parameterizedTests&r=2.5

P.K
  • 18,587
  • 11
  • 45
  • 51
1

Remember that your tests are pushing against your code. if you find that your tests look duplicated except for something like left/right, then maybe there issome underlying code that left and right is duplicating. So you might want to see if you can refactor your code to use either left or right and send a left or right flag to it.

Gutzofter
  • 2,003
  • 23
  • 26
  • I am not sure, as I see it, the structure of test code does not depend on the structure of the code tested.For example I can use the same test code to test many different implementations of a HTTP server. – Dave Dec 06 '12 at 11:18
  • The idea behind removing duplication in your test is that it can help you create a more abstract interface on your SUT. This will help you refine the objects you are testing. Plus also remember that the more duplication you have in your tests the higher the maintenance burden or higher your technical debt will be. – Gutzofter Dec 07 '12 at 00:56
0

I sometimes find myself making very complex unit tests only for avoiding test code duplication. I think it's not good to do so. Any single unit test should be as simple as possible. And if you need a duplication for achieving it - let it be.

On the other hand, if your unit test has +100500 lines of code, then it should obviously be refactored, and this will be a simplification.

And, of course, try avoiding meaningless unit test duplication, like testing 1+1=2, 2+2=4, 3+3=6. Write data-driven tests if you really need to test the same method on different data.