I don't use TDD in my normal coding workflow because it's not something that makes sense in that context (still waiting for that aha moment, but I expect it will never come).
For fixing bugs, I think it was when a more senior developer ran me through the process and crystallising the conditions that caused the bug made it seem very appealing.
For those who regularly use TDD, can you explain how it's meant to work for greenfield work?
I'm currently working on a project which we started back in Jan 2015, greenfield, with TDD through and through.
It's an API, and the first test we wrote was to successfully do a GET to an endpoint. Say /v1/user. Then we add the route, create the view and the serializer (it's a Django REST Framework API) and we get a response. Completely dummy at first, since the test only expects a 200_OK status_code. Then we add an assertion for the content, which must be an array. Now we create the model, with some fields, and make sure the wiring on the serializer is in place.
Later on, we would be adding tests that represent a bunch of business rules, like if you create a Foo object a related Bar object should exist in the database, with a given set of characteristics. And that's how we keep the system evolving, adding new features and modifying behavior, make sure everything else is in place.
EDIT: For a more practical example, one can have a look at this test file which is from a personal project of mine. Even though it's not based on the library I mentioned above, this one uses Flask, I took the same approach with it. Started with the most basic test, then moved bit by bit, by writing tests that would depict the outcome I wanted.
I can see how it would make sense for a REST API where you have a defined endpoint and response format, and actually not a use case I had considered TDD for, nice one.
My issue with TDD is how it's meant to apply, if at all, to a task when you're adding a high level feature and TDD implies you write tests for the units (functions) first. How do you know what functions to have or do you go full waterfall and do all the design up front? In that kind of case, I go for TLD but TDD advocates really don't like that and I'm not sure why.
It's hard to describe it without a clear example, but I can assure you I have created and evolved dozens of high-level features using TDD. And, yes, it's true that TDD needs clear and defined requirements, but that does not mean that these requirements are final.
You see, if you have any given requirement, fully-detailed or not, and you need to get implementing it, you will always have a scenario to implement, if it was not defined by the Product Owner (or similar role) you will take a guess and go for it, right? So, you create a test for that scenario. Make it pass. Validate the feature with the P.O.
Oh, that's not what he had in mind? Then ask him to point it out how it is not. Now you have a clear requirement. Modify the test. Change the code. Validate the work.
TDD, at its core, is about incremental change. Don't take giant leaps of faith, instead take small, clear-view, steps.
Finally, if you could provide me with the scenario you have in mind I could help you out with it. Hit me up on Twitter. :)
I don't think I've ever heard TDD talk about incremental steps, it's always been the focus on at the unit level.
I don't have an example at the moment, this is mainly from my last job where I couldn't figure out how to fit TDD in for new features. It was still very much a start up in how the day to day went so devs were given a feature to implement and that was it. Aside from the requirement, there wasn't even a function to call so you couldn't really write a high level test at the start.
You seem to be confusing unit testing with TDD.
Unit tests are one type of tests, the bottom layer of the Test Pyramid, which also defines the Services and UI tests.
Whereas TDD means Test-Driven Development. Simply put it says that you should write a test first, and only then you should write the code to make it pass. After that, you should refactor, add a new assertion (requirement), making it fail again, and write the code to fix it. It can be any kind of tests. An integration test that talks to a 3rd-party system, a unit test that mocks all external calls but asserts that the inwards of an endpoint behave properly or an automated UI test that validates user input and the error message display feature.
It seems people these days have not read Test-Driven Development. They end up getting to know TDD from different places, with different levels of adoption, etc. I highly recommend anyone to read it. It is simple, clear and small. And on top of that is super effective. I usually name it as one of my career-changing books (the other one being The Agile Samurai).
This is all good - one quick clarification: a Unit test does not necessarily test a function/class/object/whatever.
It tests a unit of behaviour.
TDD does not mean adding one test per function/class/whatever. It should mean writing a test that describes the behaviour of your program, and then writing code that implements that behaviour.
We're a place where coders share, stay up-to-date and grow their careers.
We strive for transparency and don't collect excess data.