11 ms·
> While not traditional TDD or likely not a new concept, I’ve done something I’ve dubbed TLD (Test Led Development). Rather than writing a whole smattering of t
by Learner100 4y ago
> While not traditional TDD or likely not a new concept, I’ve done something I’ve dubbed TLD (Test Led Development). Rather than writing a whole smattering of tests and then coding until they all pass, TLD focuses on ping-ponging between test and code.
TDD commonly gets mischaracterized as a two-step process of writing a suite of tests upfront, then writing some implementation to make them all pass. It's actually much closer to what is described as TLD in the post. You write a failing test, do the bare minimum to make it pass, refactor if appropriate and start the cycle again. It's a development process that will (in theory) produce a high quality implementation and suite of tests at the end.
- Jtsummers 4y agoI was going to say the same thing. His characterization of TDD as "writing a whole smattering of tests and then coding until they all pass" suggests he's gotten some bad information about TDD. Sadly common, though. His TLD is pretty much TDD except he doesn't mention refactoring after passing the tests. Even leaving a failing test at the end of the day as a kind of "todo" for the next day, I'm pretty sure Beck mentioned using that idea in his TDD book. EDIT: Found it finally. From Beck's Test-Driven Development by Example (page unknown, ebook copy, chapter 27): > How do you leave a programming session when you're programming alone? Leave the last test broken. > Richard Gabriel taught me the trick of finishing a writing session in midsentence. When you sit back down, you look at the half-sentence and you have to figure out what you were thinking when you wrote it. Once you have the thought thread back, you finish the sentence and continue. Without the urge to finish the sentence, you can spend many minutes first sniffing around for what to work on next, then trying to remember your mental state, then finally getting back to typing. > I tried the analogous technique for my solo projects, and I really like the effect. Finish a solo session by writing a test case and running it to be sure it doesn't pass. When you come back to the code, you then have an obvious place to start. You have an obvious, concrete bookmark to help you remember what you were thinking; and making that test work should be quick work, so you'll quickly get your feet back on that victory road. > I thought it would bother me to have a test broken overnight. It doesn't, I think because I know that the program isn't finished. A broken test doesn't make the program any less finished, it just makes the status of the program manifest. The ability to pick up a thread of development quickly after weeks of hiatus is worth that little twinge of walking away from a red bar.
- alasdair_ 4y agoBeck had this process (test, code, refactor) in place in his original eXtreme Programming book, which helped spawn the whole “agile” movement. It’s kind of cool to see how the process was refined over time. It’s also sad to see how twisted people’s idea of both agile and tdd have become, usually because they never read the source material.
- switchbak 4y agoHaving been involved in that world from pretty early on, I must admit I'm pretty appalled at the odd interpretations I get both on HN an in real life. I'm not sure where to put the blame, but "Agile" does get a particularly bad rap these days. Mostly I'd say a corporate watering down of Agile concepts, money hungry consultants who didn't know much, and I h ave a personal dislike of how most of SCRUM tends to be implemented. TDD in particular has fallen off the map in a way that I find very surprising. Generational amnesia I suppose.
- mpweiher 4y ago> corporate watering down of Agile concepts I think inverting agile concepts is more accurate.
- Jtsummers 4y agoIt's very common. When I was working at a USAF base they decided to adopt Lean. What they actually implemented was almost the exact opposite of Lean in every way, almost comically so. A key element of Lean is empowering the workers to improve the processes. Let them come up with ideas that improve things and run experiments (guided by management perhaps, but not directed by). But the way USAF did it, the managers would watch a process being done, identify "wasted" movement, and then rewrite the process/procedures to eliminate that wasted movement. It was clearly just Scientific Management but being called Lean because they "leaned out" the processes. Naturally, the actual workers did not like coming in every other week and having to learn their job all over again. After a while they still held "Lean Events" but by then it was for show rather than to actually effect change in how things were done.
- tsuujin 4y agoThere is an awful lot of discussion out there about TDD that does not mention the Red-Green-Refactor concept at all. I think that really changes the impression of TDD.
- eyelidlessness 4y agoIt certainly did for me. A former teammate did a lunchtime session on TDD, which introduced me to the concept. I’m self-taught, and my foray into testing was very much a matter of trying to bolt tests on after the fact. So this concept of red-green-refactor was wild to me. A little intimidating at first, and I didn’t adopt it right away. But when I did, it not only made testing better, it made my code better too. Not only because it’s more testable, but because it makes me think about the interface first, and the implementation truly as a black box as much as possible.
- jpatt 4y agoOne of the most disappointing parts of moving away from .NET for work was the loss of NCrunch. That one tool made Red-Green-Refactor a breeze. In Ruby/Java it is certainly a bit more of a chore to remember to do.
- sixstringtheory 4y agoFirst time I've ever heard of that tool, it looks incredible. I wish Xcode had some of that functionality. It's close in some ways, all the building blocks are there.
- sn9 4y agoHoly shit. Link for the lazy: https://www.ncrunch.net/ https://www.ncrunch.net/
- tester756 4y agoI don't see benefits of R->G step
- 4y ago
- beached_whale 4y agoCombine this with usage testing. At least for libraries, which I seem to end up writing a lot of, writing usage tests that don’t necessarily test units but functionality. Doing this early on helps flesh out the friction in usage and will help with testing as it often can have broad coverage. It also, now sits as a potential example for usage in documentation.
- pydry 4y agoThese are way, way more useful than unit tests IME.
- JoeNr76 4y agoEven after all these years, people still have this misconception about TTD. No idea where it came from.
- nonethewiser 4y agoDogmatic evangalizers
- jdlshore 4y agoMicrosoft. It came from Microsoft. https://www.jamesshore.com/v2/blog/2005/microsoft-gets-tdd-completely-wrong https://www.jamesshore.com/v2/blog/2005/microsoft-gets-tdd-c... (Well, they popularized it.)
- lamontcg 4y agoI'm not sure if I go even further or not, but I'm perfectly happy to write code first and then write tests. And I will sometimes not write tests until I start debugging code and start thinking to myself "is it a buggy bit of this function I haven't tested yet?" and so I'll write the test to prove it isn't that bit. With code I'm getting paid for I'll write more tests up front and won't skip that step, but for code I'm playing with then as long as I'm just enjoying myself writing code I'll write zero tests for awhile and just code. Then as I hit the debugging/refactoring step I'll do the backfilling as a form of debugging. Often I'll write the code that fixes the bug, then write the test, then quickly and temporarily revert just the code to ensure that the tests fail and then proceed. That gets you the same safety check as doing it the test-driven way to validate your test actually tested the right thing. I really tend to hate "thou shalt start by writing tests" as some kind of immutable golden rule. It does always feel good to me when I just naturally wind up doing it, but forcing myself to do it every single time just isn't any fun at all. At the same time tests are absolutely essential when it comes to refactoring and debugging. When you get too far out ahead of yourself with code then you start needing to shore up the foundations and use tests to eliminate bugs in the code that you've already written. Some code though is obviously correct enough that in personal hobby projects I won't ever code tests for them (unless I do hit the point where I start to doubt their correctness due to some funny bug at which point the situation has changed so I add some tests to prove it one way or the other). The whole point of this though is that the tests are always serving me and they aren't in the drivers seat quite the way that all the TDD 101 blog posts like to ram down your throat and which I suspect turns people off from that approach so much. The end result is also that you'll tend to wind up with the tests that are actually useful, covering the code that is particularly hairy or essential and the edge conditions that you really need to make sure to get correct, and you wind up having a test suite which is composed mostly of useful tests instead of all the largely useless ones that infect codebases. I'll also happily omit tests on lower level functions that are well tested at the level above them, because I don't need to test the same thing at 18 different levels (again, for professional use I'm more likely to include tests at every level if they're fairly mechanical to produce). I also have a flexible definition of what the system under test is, which often encompasses more than just the immediate object that I'm testing and I don't bother wasting mental effort thinking about how to mock the whole world. I don't know what kind of TXX that is. I still wind up with tests, they're legitimately essential to have, I just don't get there via some prescriptive route. I wind up with good code coverage, but I don't necessarily wind up with it looking as comprehensive as rotely banging out lots of unit test. I typically wind up with tests that I know are useful because they were produced by hitting actual bugs or where I had real questions about the behavior of the code and needed to assert some invariants and prove the code worked.
- danielvaughn 4y agoAlso, as with almost everything else in life, it's important to not be so dogmatic about the "rules". Everything is negotiable - I'd never recommend literally only ever follow the strict red-green-refactor workflow.
- garethrowlands 4y agoI recommend people learn the “proper” technique, even if they don’t apply it in every context. As they say, _you need to be in the mould to break the mould_.
- User23 4y agoTDD properly done is a 4 step iterative process. 1) Write test. 2) Run test and see that it fails. If it doesn't fail figure out why and fix it so it does. Either the test is wrong, or what you're testing is trivial. 3) Write code so test passes. 4) Refactor code so it's good. Next go back to step 1. Step 4 is the trickiest and most important part. Refactoring transformations must be such that they do not invalidate the results of step 3. But if you don't execute step 4 properly you'll end up with crap code.
- hbrn 4y ago1. 90% of the time what happens during step 4 is realization that your tests are crap. So you have to throw them away and start from scratch. Tests are not helping with refactoring, they are inhibiting it. 2. Step 4 is indeed the most important part, and yet TDD priests and scriptures don't cover it at all. TDD is actually distracting you from what's important, because it focuses on steps 1,2,3. Eventually you'll become disciplined enough to not get distracted, and you'll think that TDD works. But the reality is that you never needed TDD in the first place.
- User23 4y agoI’ve used TDD as a kind of proxy for predicate transformer semantics. Test first is a way of sneaking specification first into a team’s workflow. But let’s be clear, overspecification is bad specification! And that’s why as you say a lot of times the tests get in the way: they are overly specific and thus inhibit desirable refactoring. In my experience it’s better to err on the side of underspecifying. Most everyone agrees with me on that, since the total lack of specification you generally see is a species of extreme underspecification. Don’t think I’m being snarky either, even that level of underspecification can be appropriate in the exploratory phase, although I think it’s generally worth the effort to start from a specification that’s at least slightly more restrictive than the always true predicate.
- hbrn 4y agoYeah, that's how I think about testing in general. Tests should be a reflection of your specs. But if your specs are bad (most of them are), you should wait for them to improve. Not carve them in stone.
- nine_k 4y agoTwo-step TDD can work if you implement a well-known, stable spec, say, an IP stack. Such cases are relatively few.
- DougMerritt 4y agoRight, and in the absence of a well-known stable spec, then it seems to me that TDD (and related) is just a variant on the very old and very discredited "Waterfall Model" [0] [0] https://en.wikipedia.org/wiki/Waterfall_model https://en.wikipedia.org/wiki/Waterfall_model
- Jtsummers 4y agoHardly, if it was a variant of Waterfall you wouldn't have testing mixed in with the development of new code. Waterfall has explicit barriers between the development and test phases which is why those systems usually turn out to be clusterfucks (unless they're small or you have, usually by luck, a correct specification). In real-world Waterfall projects (which I have suffered through, worst was a multi-billion dollar disaster for tax payers, and a multi-billion dollar success for the contractors that we took it over from), testing happens after development which means you have no useful feedback while developing that you are building the wrong thing or building it incorrectly.
- DougMerritt 4y agoI had in mind that Waterfall separates architecture design from detailed design from implementation, and in the above case, the spec being precise and finished certainly means that the top level design (or architecture or whatever) is 100% finished before the TDD development begins. Hardly a reason to downvote me.
- Jtsummers 4y agoBut TDD does not separate architecture design from detailed design from development from testing like Waterfall. It integrates them, or at least enables integrating them. It is literally not Waterfall, which is an idealized (and thus its primary flaw) process predicated on that strong separation that TDD deliberately breaks. TDD is predicated, instead, on the idea that we don't know everything up front. Otherwise we wouldn't need or want to write tests in the middle of development. As to the downvote, I guess you thought it was me, it was not. But I don't plan to upvote you either.
- heywhatupboys 4y agoit is just the classic arrogant blogger isn't it? "I rediscovered something obvious, mischaracterised what exists already, and I make up a term and pretend like it is novel, and credit myself with it" Hoping someday to be the new Fowlers, surely.
- finnh 4y agoYou're getting downvotes for snark but you're not wrong.
- heywhatupboys 4y agothe truth is ill heard lol
- brightball 4y agoYep, agreed. I tend to just code a single giant integration test where I map out the happy path to start with, and then when it works code tests for known edge cases at specific parts. Keeps me focused.
- sorokod 4y agoIt's a development process that will (in theory) produce a high quality implementation and suite of tests at the end. In fact the theory is that this approach will produce a high quality design as an emergent property. This is an extraordinary theory that requires an extraordinary proof - one I haven't seen so far.
- jdlshore 4y agoIt's fairly straightforward: TDD is red/green/refactor, which means you're working in very small steps (about a minute or two each) and thinking about design during two out of three of those steps. During "red", you're thinking about the design of your public interface. During "green," you're focusing on implementation. During "refactor," you're thinking about how to improve the quality of your implementation and how to improve the overall design, and making those changes. If you believe that spending a lot of time thinking about and improving your design will produce a high-quality design, then TDD will produce a high-quality design. QED. If you don't accept the axiom, then it's a longer discussion, but that's the proof, and my experience is that it does in fact work. (If you're looking for a rigorous study and proof, you won't find it, because there are no rigorous studies that formally prove what creates high-quality design. Partially because there is no formal definition of "high-quality design" in the first place.)
- sorokod 4y agoTDD is obviously a hill climbing strategy as far as the end result is concerned and the outcome has the associated baggage. "Refactor" is named this way to emphasize that changes you are making are closely related to the tests you already have and the tests you are about to introduce. That does not leave enough space to justify a QED. If you choose to design beyond that, the process stops being TDD - at least as described by Kent Beck
- hbrn 4y agoTDD has several big issues that lead to bad design: 1. It assumes your spec is good and rigid. In reality, most specs are shitty and fluid. And your first understanding of spec is wrong. 2. It assumes your first implementation of the spec is good enough to justify automated testing. 3. It leads to high test coverage which inhibits refactoring (despite zealots telling you otherwise). 4. Almost always it leads to obsession with testing, which leads to a ton of unnecessary complexity (e.g. dependency injection for the sake of testing, weird practices like "don't mock what you don't own", etc) I always thought it's great that it forces you to think about public interface, but I came to believe that thinking cannot be forced with a ritual.
- sedatk 4y agoTDD may not be a two-step process, but its first step is always "write tests" hence the term test-driven, which author clearly diverged from.
- superjan 4y agoI don’t either. But somehow TDD is considered the sanctioned way to work with unit tests, and that it is almost pointless to write tests unless you’re doing TDD. I am relieved to read that I am not the only one.
- garethrowlands 4y agoTDD's first step is write *a test*, not "write tests". Batch size of one. If it was "write tests," it'd be more waterfall.
- sedatk 4y agoStill, you write a test first which, again, the author diverged from.
- lowbloodsugar 4y agoIt always amazes me when someone has the audacity and sheer lack of curiosity to decide that something they've read about means something else, and then "improves it" and "I've done something I've dubbed" to what the real thing is in the first place. TDD has a wikipedia page [1] FFS, which is the #1 hit entering TDD into google, and which very clearly lays out that TDD is a test/code cycle (red, green, refactor anyone?). What the author claims is TDD is called TFD (Test First Development). How do people develop this kind of hubris? [1] https://en.wikipedia.org/wiki/Test-driven_development https://en.wikipedia.org/wiki/Test-driven_development
- zulu-inuoe 4y agoWhat I find so interesting is that we all effectively do TDD, except without commitment to it - Imagine writing some code and not at least testing it out with several test cases. The absolute bare minimum should be for you to write those informal tests down and commit them to the repo. That's some golden knowledge I've gotten over time It just requires the overhead of setting up the test runner
- spc476 4y agoBut the orthodoxy seems to be write the tests first, see them fail, then write code, not write a little bit of code, write some test cases, repeat. How "test first, then code" works in a compiled language like C++ or Rust be beyond me (unless I'm taking this to a literal conclusion).
- thraxil 4y ago> How "test first, then code" works in a compiled language like C++ or Rust be beyond me It's not really any different than in dynamic/interpreted/weakly-typed languages. "Writing the test" for a function sometimes just includes writing a method/function with the appropriate type signature that does nothing (maybe returns a dummy value). Forcing you to view the code you're implementing from the viewpoint of someone calling that code from the very beginning is one of the advantages/goals of TDD. If you find that it's difficult to set up the objects/data you need to write a test, eg, your code has a bunch of implicit dependencies on other components being in a particular state or it takes a ton of arguments that all have to be constructed, that's usually a strong indicator that you should rethink the design. You're getting early feedback on your API design before you waste time implementing it.
- mattm 4y agoSadly, this was my impression of TDD for years. I always thought that writing every single test case up front would never make any sense. It was only when I read the original definition of TDD from Kent Beck that it clicked. I use it quite often now and it makes development a lot easier.