DEV Community

Cover image for Why TDD?

Why TDD?

Chris James on October 26, 2018

It's difficult to write about Test Driven Development (TDD) without rehashing what others have said but it helps me to organise my thoughts aroun...
Collapse
 
dmerejkowsky profile image
Dimitri Merejkowsky

Nice article! I would have used another math example, though:

Realize that (7.x + 7.y) is in fact 7.(x+y)

I prefer this example better because:

  • there's no automated rule to do it (contrary to your fraction example)
  • the result of the factorization seems easier to read

and I think it better reflects what refactoring is.

I want to explore the design first, then I write my tests afterward.

There's a video from destroyallsoftware that explains how you can explore first, then throw all the code away, and rewrite it following TDD. It's interesting to watch.

Cheers!

Collapse
 
quii profile image
Chris James • Edited

how you can explore first, then throw all the code away, and rewrite it following TDD

For sure, sometimes its hard to make a start with something small and you need to explore the problem space a bit. So long as you have the discipline to throw that code away its all good.

I like your maths example but I was just trying to keep it as simple as possible to show the relationship between factorisation and refactoring :thumbsup:

Collapse
 
gypsydave5 profile image
David Wickes

I have found Unit Tests and TDD in general [...]

not the same thing - you can do TDD with end-to-end tests. It takes longer, it's more BDD, but you can do it. Anyway...

[...] compared to automated Functional tests based on requirements.

The assumption being that the unit test aren't based on requirements? I think if you read the author you'd see that what you think of as a functional test is more like a unit test for him: testing the 'business' (i.e. the really important what it ought to do) logic of a program. Not the incidental, one-test-per-function/method/object/class craziness.

As to Coplien's diatribe, it's pretty much 'bad tests are bad' and that coverage won't save you in the end. Well, nobody said it would. If you're testing at the wrong level 100% code coverage is actively harmful. If your test suite is more complicated than your code, you're doing it wrong. Doesn't mean you stop doing it and start driving everything through a web browser and testing your HTML got written right.

more assertions in code.

Doesn't that just give you runtime errors to tell you when things are wrong? You actually do this? I thought it went the way of the goto...

Collapse
 
quii profile image
Chris James • Edited

Yup, I have been in many a codebase, reluctant to refactor because of the sheer pain of having loads of tests complain at me.

But some would take this as an excuse to not write unit tests at all and only write integration/functional tests, which bring a number of different problems instead. Namely harder to read, write and slower to run and debug.

The real solution is to write good unit tests :)

Collapse
 
atsteffen profile image
atsteffen

In trying to relate the relationship I see between testing and refactoring, the expression "It takes two to tango" keeps coming to mind. If the process of keeping software flexible and reliable under an onslaught of new features is a complex dance, then testing and refactoring are the key dance partners. They rely on each other in a complex and iterative way. You need tests to refactor effectively and you need to refactor to test affectively.

Our team learned the hard way that applying semi-rigorous TDD overtime without giving sufficient attention to coupling (and SOLID principles in general) can lead you down a path towards test-suite maintenance hell. We failed to control the size of monolithic components and allowed ourselves to hack in more functionality. We became burdened by brittle tests (chalk full of mocks) and complex test utilities. At some point you start to lose the benefits of TDD, and even worse you start to lose faith in TDD (unit testing in general) from developers and management.

We were fortunate enough to be given time for some major technical debt pay downs. After some major modularization, levelization, and refactoring to adhere to good design principles, clean and clear testing paths re-emerged for code that was previously seen as too difficult to test. Disciplined testing of changes in that code could again be fast and reliable.