top | item 24302234

Ask HN: What resource do you recommend to start with Test Driven Development?

11 points| rednosehacker | 5 years ago

12 comments

order

Normille|5 years ago

I've never been able to get my head round the concept of Test Driven Development. Maybe it's because, every time I've seen an example of it [which, like the one you linked to] are usually aimed at explaining the concept, they seem like a lot of extra work for questionable benefit.

For example: I've got some 'multiplying' code that should return 4 when passed 2 and 2 so I'm advised to write an "assert" test that returns 4 when I send it 2 and 2. But what happens if I send it just 2?... or 2 and 2 and 2?... or 2 and 3?... or 2 and W? None of the examples of why it's so vital to write tests ever seem to cover this kind of thing? They just seem to show a test which returns the correct answer when given the correct data. But aren't many of the problems with software caused when a piece of code receives the wrong data in the first place?

Genuine question. I've read so many times about why I should practice TDD that I believe it in theory. But I've never seen a beginner's example that has actually seemed like it would provide the claimed benefits. Why is writing a test that takes one single specified input and checks for one single specified output "better" than just writing my code and then trying to break it by [metaphorically] hitting the "Do Not Press!" button, by throwing as much incorrect, malformed or otherwise "wrong" data at it, as possible.

chris_j|5 years ago

For me, TDD is useful for a number of reasons, not all of which are directly related to testing.

Firstly, it forces me to think about the "what" and not the "how". When I start writing a unit of code, I write a test before I start writing the code that will make that test pass. This forces me to think more rigorously about what I want the code to do. The unit of code that the tests are driving might be as small as a single class, in which case I'm forced to clarify precisely what the responsibilities and collaborators of that class are. Or the unit of code might be as big as a whole service, in which case I'm forced to specify precisely what that service does, hopefully in term that the customer would understand. I find that in both cases (and especially the latter case), the tests become an executable specification of precisely what my unit of code (class or service or whatever) is meant to do. If the tests are written clearly then it's possible to read the tests and understand that specification.

Secondly, when doing TDD, especially when the tests drive small units of code, it forces me to design the code better than I perhaps would have done otherwise. If I have a class with many responsibilities and dozens of collaborators then it's really hard to write unit tests that drive that class. But that same class is also going to be pretty difficult to understand and to maintain when another developer opens it up again in a few months. TDD forces (or strongly encourages) you to design clean, simple units of code with a small number of collaborators, which tend to be easier to work with as time goes on. I had heard of the SOLID principles and Kent Beck's four simple rules of design but it was TDD that hammered home the fact that they are necessary if you want to make it easy to write code that is easy to work wit and easy to change.

Thirdly, a crucial part TDD is refactoring. (The TDD cycle is to write a failing test, make that test pass as simply as possible and then, crucially, refactor the code so that you're happy with the way that it looks.) The way I used to write code, the quality and design of the code would tend to slowly deteriorate over time as I kept changing it and I wouldn't know how to prevent that. Nowadays, I refactor all the time and the refactorings that I do tend to be very small and very safe. If the quality of the code got a little bit worse in order to make a test pass then I gently nudge it back in the right direction with a few safe behaviour-preserving refactorings which, nowadays, my IDE mostly does for me.

Fourthly, for me, TDD guarantees 100% test coverage of the behaviours that I care about. For every behaviour that I want my code to have, I write a test before I even implement that behaviour. I've used code coverage tools in the past but meh, all they do is tell me which lines get executed when I run all the tests. TDD gives me an executable specification of the behaviours that are important to my customers, with complete coverage of those behaviours, and they helpfully tell me if that specification is ever violated.

In sum, I find it very difficult to explain why TDD is useful. It took me 18 months or so to get over the hump and learn to do TDD well enough that it was giving me really significant advantages over the way that I worked before. After that, it kind of clicked and I finally got why this stuff was so powerful. But I don't think you can explain it reductively. TDD is a set of practices that you follow that give you emergent properties such as more correct, more maintainable code with better test coverage. If you're familiar with Cynefin (https://en.wikipedia.org/wiki/Cynefin_framework), I'd put learning TDD in the Complex domain, where cause and effect are obvious but only in retrospect. And now that I've learned to do TDD effectively, there's no way that I'd go back to the way that I used to work before.

rednosehacker|5 years ago

Hey Normille,

I am glad you asked !

"Genuine question. I've read so many times about why I should practice TDD that I believe it in theory. But I've never seen a beginner's example that has actually seemed like it would provide the claimed benefits."

You need to find ressources to show you. I think when you see a thousands lines long codebase (divided in modules, classes, files, ...) with a test suite which fail immediately when a feature - you are not even working on so you where not meant to test immediately - breaks because of your current refactoring you thought was cool... Voila!

"Why is writing a test that takes one single specified input and checks for one single specified output "better" than just writing my code and then trying to break it by [metaphorically] hitting the "Do Not Press!" button, by throwing as much incorrect, malformed or otherwise "wrong" data at it, as possible. "

Did you see the test to fail before you write your code ? Maybe you don't need to write it haha. Maybe your test don't fail for the good reason. If you don't write your test before to see it to fail for the good reason, how are you sure you write code to actually do what it is supposed to ?

To me learning TDD is like learning martial art (I talk about my own experience haha). You need to practice. Some say it need discipline to stick to it enough, sometimes you are not convinced but you just push the experiment a bit further and there you get it ! Maybe you like to read, to watch, to just listen, or you need a mentor, someone experienced ? Hopefully there is a lot on internet.

I have three main ressources to get into TDD :

1) The book "Test Driven Development by Exemple" by Kent Beck

2) The screencast series from James Shore (there are more on his website) https://jamesshore.com/v2/projects/lunch-and-learn/increment...

3) To help to start with refactoring, the Book "Clean Code" by Uncle Bob Martin

You can learn from blogs how to use test frameworks, what is a unit test, what does it mean to test first. But TDD is more, than that. The examples given to discover TDD are not complex. But it is enough to get the workflow and sometimes to feel the pros. In TDD the order of your tests is relevant and it is to me the main point on which I have to improve, the way you refactor is relevant too. I mean, sometimes I don't end up with the same implementation after doing the same exercise twice or more.

Then you go on more and more and more complex, with practice. ;)

Going back on you example:

"I've got some 'multiplying' code that should return 4 when passed 2 and 2 so I'm advised to write an "assert" test that returns 4 when I send it 2 and 2."

This is the nominal behavior (happy path) of your program. But I would not make it my "First Test". I would start with giving 0 and 0 returning 0. Then 1 and 1 returning 1. Then 1 and 2, ect... Each time you try to code only what the currently red test case needs to pass. Don't try to cover all the possible cases to early, it is not a sprint, it is a marathon.

"But what happens if I send it just 2?... or 2 and 2 and 2?... xxxxxxx or 2 and W? None of the examples of why it's so vital to write tests ever seem to cover this kind of thing? They just seem to show a test which returns the correct answer when given the correct data. But aren't many of the problems with software caused when a piece of code receives the wrong data in the first place?"

These are the error path of your program if it is supposed to handle them. As you said it, you will have to write tests to write code to cover them. One at a time : lower number of arguments, wrong input type, ...

chris_j|5 years ago

TDD by Jason Gorman is a nice (and free) introduction: http://codemanship.co.uk/tdd_jasongorman_codemanship.pdf (disclosure: I did Jason's ~TDD training course a number of years ago and gained a lot from it - but the book is useful on its own).

A book that explains TDD in the context of a much larger example application is Growing Object-Oriented Software, Guided by Tests by Steve Freeman, Nat Pryce (sometimes referred to as "The GOOS Book").

One of the difficult things about TDD is that it takes a long time to learn to do it effectively. I personally didn't really "get" TDD until I paired with a few much more experienced developers, and this forced me to rethink the way that I approached writing software. Ultimately, it caused me to significantly level up my coding skills and I wouldn't go back to the way that I used to write code before.

rednosehacker|5 years ago

Experienced developers are key to share knowledge ! It was the most effective way for me learn about TDD

moksly|5 years ago

I’d recommend you stop, but if you’re really going to go through with it. One of the best resources is the testing goat book. It’s tool specific (django/python) but it’s so good I think it’s worth it just to get into the subject.

https://www.obeythetestinggoat.com/pages/book.html#toc

tmaly|5 years ago

I would highly recommend reading Kent Beck's book. He walks you through the entire process. He has two examples, one in Java and one in Python. Both are readable even if you do not program in either language.

sloopy543|5 years ago

None! Don't do it! It's a foolish and dogmatic way to make software, and a waste of time.

rednosehacker|5 years ago

Sorry you had a bad experience with TDD ! Mine is pretty good actually.

But life is long, anyone could benefit from trying before decide wether or not such method/techno fits ;-)