38 ms·
In the earlier days of the ruby community, I feel TDD was seen as gospel. And if you dared say that TDD wasn't the way (which I always felt), you'd feel like yo
by atonse 9y ago
In the earlier days of the ruby community, I feel TDD was seen as gospel. And if you dared say that TDD wasn't the way (which I always felt), you'd feel like you were ostracized (update: just rewording to say, you'd worry that it would hurt you in a job search, not that people were mean to you). So I never spoke up. I feel like I was in the TDD-bad closet.
I absolutely think _tests_ are useful, but have never found any advantages to test-DRIVEN-development (test-first).
But part of that is probably my style of problem solving. I consider it similar to sketching and doodling with code until a solution that "feels right" emerges. TDD severely slows that down, in my experience, with little benefit.
What I've found works really well is independently writing tests afterwards to really test your assumptions.
- jcbrand 9y agoMy experience is that whether one is writing tests first or not, the code that is being written does get tested one way or the other. In other words, people (for example web-devs) who don't write tests first tend to "test" their code by clicking around in the browser to see whether they get the desired effect. Writing tests, running them and then iteratively updating/fixing the code is IMO so much more efficient than manually clicking around to test one's code.
- sbov 9y agoClicking around in your browser is more akin to an integration test though. TDD is about unit tests, not integration tests.
- briandear 9y agoI disagree. I and others (such as Thoughtbot) often start by writing integration tests first. It’s known as outside-in testing. TDD by started by with low level tests is just backwards. Outside-in helps you clarify the design from the perspective of the user. https://robots.thoughtbot.com/testing-from-the-outsidein https://robots.thoughtbot.com/testing-from-the-outsidein
- ericgunnerson 9y agoATDD or design-driven development starts with integration tests first, and can be a great thing if written by the PO, as you can pretty much skip acceptance testing. It addresses a different concern than unit testing, however.
- wolco 9y agoYou will miss items if you don't visually inspect even in a ttd application. Tdd is a net to catch some expected bugs but should not be the only tests.
- stale2002 9y agoExactly this! It is literally faster to write the test, then to manually go around hitting your endpoint.
- felipeccastro 9y agoAgreed, but I would add there are specific scenarios where doing the test first really helps - when you're building a routine complex enough that you can't think upfront how you'll do it, like a complex calculation for example. In that case, at least thinking about the tests first and making one by one pass helps you breaking down the problem into smaller, easier parts.
- spdionis 9y agoThis is the best situation for a TDD workflow, and the only situation I myself use TDD in. If you have complex calculations or data transformations or you know well the expected input and output of what you're trying to do TDD actually speeds you up by allowing you to iterate faster on your solution. If you're writing glue code between a number of other application components (90% of development in web backends) then TDD loses a lot of benefits.
- aeorgnoieang 9y agoAFAIK, all of the TDD proponents would agree that lots (most?) glue code doesn't need to be covered by unit tests.
- flukus 9y agoI think this is the result of code coverage metrics that are often advocated. When you need some arbitrary level of coverage you have to write tests for glue code. It may be different in dynamic languages, but in statically compiled ones the compiler does all the testing required for glue code.
- mannykannot 9y ago> In statically compiled ones the compiler does all the testing required for glue code. It cannot, for example, tell if you have glued things together in the wrong order. ...but a test of the glue code alone will not necessarily tell you that, and you will waste a lot of time mocking the lower-level functions to perform a standalone test on it. It is better to test an assembly as a unit, which will test the glue code (and give you a coverage count for it.) In many cases, it will also test your low-level code without you having to write so many mocks and harnesses. I think you can find TDD zealots who insist that you have to write tests for every little piece in isolation, but this takes a lot of time, especially if you have to write a lot of mocks to do it (and testing with mocks is error-prone; the mocks may conform to your invalid assumptions.) There is never enough time to test everything, so excessive unit testing means giving up some potentially more informative integration tests. As in most aspects of development, good judgment matters more than strict obeisance to the rules.
- heifer2822 9y agoI think TDD helps or doesn't help depending on the circumstances. The problem is the people who claim there's One True Way. For instance, if you were working on a big app and there was a bug, writing a failing test for that bug and then looking into the fix is very helpful. But if you're starting a new project from scratch with loose requirements, or worse yet, building a prototype, starting with tests would be a waste of time at best.
- moron4hire 9y ago> if you were working on a big app and there was a bug, writing a failing test for that bug and then looking into the fix is very helpful. Well, first of all, that's not TDD. TDD is Test-Driven Development. In your scenario, you wrote the test after the code in question. In TDD, the whole point is to write the test before the code in question. So the value in your scenario is only insofar as the test is a useful way to quickly exercise the code in question. For example, if we have some sort of customer-facing website and there is a bug in how they save their user profile, it will be easier to test fixes to the problem if we don't have to go through the full flow of logging into the site, navigating to the profile, making edits, and clicking save all the time. But having an XUnit-style framework for running that test is not necessary for that.
- luhn 9y ago>> if you were working on a big app and there was a bug, writing a failing test for that bug and then looking into the fix is very helpful. > Well, first of all, that's not TDD. TDD is Test-Driven Development. In your scenario, you wrote the test after the code in question. In TDD, the whole point is to write the test before the code in question It's a regression test, which is definitely TDD. The code in question is the fix for the bug, the test verifies that the bug is fixed and will remain fixed.
- tokenizerrr 9y agoNo that is just regular testing. TDD is about writing tests first, before other code.
- moron4hire 9y agoLike the author in the original article, I used to be all about TDD. Now, I like to tell people that "I'm apostate in the ways of TDD". I specifically use the language of religion because I think strict adherence to TDD is itself a religion. I somewhat disagree with the statement he makes, "we know that most developers do not have great design/refactoring skills". I've certainly worked at places that all but ordered me to never refactor code, and I suspect my experience is very far from unique. They thought "refactoring" was a made-up word that programmers used to cover up dicking around and wasting time. From a non-programmer's perspective, all they see is the programmer spending several hours with the end result being that nothing has visibly changed. Mind you, no visible changes is the ideal case for refactoring, so you have no easy means to explain why that is wrong. He says that TDD only works if the programmer is good, but I think it's more the case that test-first only works in cases where we have very well-defined problems. For these TDD trainers that he's talking about, they've gone through the same problem set over and over again. For an experienced programmer, if you're encountering the same sorts of problems, you know what the pitfalls are and can look ahead, even if you aren't violating YAGNI, you can still do yourself some favors. To put it to a specific metric, if you have a specification from which you're working, you basically have an answer sheet for your code. You can then write code that automatically verifies that your code matches that answer sheet. The more like "having a spec" your problem is, the more likely TDD will work very well for you. But I don't do that kind of work very frequently. Most of my work is extremely experimental, fluid, and in a lot of ways, its behavior is arbitrary. Does a padding of 5px or 10px look better? Should the surface of a polygon be more or less smooth? Should running over the ground be 3m/s or 5m/s? Should the user have two hand tools that are identical, or should they be different tools? A lot of it has to be visually verified, because it would otherwise require a computer vision algorithm of some kind to know that the code I wrote to go from text to screen pixels worked correctly. It's much more design than development, just that design is expressed in code rather than in Photoshop PSDs. As a result, I focus much more on REPLs, saved REPL sessions, demo code, and making the iteration time between code change and test run as narrow as possible. What I think TDD provides (poorly) for people in this situation is this REPL-like behavior, in languages that don't particularly provide a good REPL (let us consider RoR a separate "language" from Ruby, in this case, as the expectations tend to be completely separate). So if I'm writing an implementation of a rope data structure as part of a syntax highlighting text editor that renders into a WebGL texture, yeah, I have tests for that. Ropes and text editors are well-understood data structures, there's lots of information available about them, and there are right and wrong answers to what operations each should have and how they should behave. But that's not where the work ends. I still need to figure out how to let people interact with that text editor, be it with touch gestures or motion controllers or a keyboard or a gamepad or what have you, and that has no easy-mode.
- stale2002 9y ago"What I've found works really well is independently writing tests afterwards to really test your assumptions." Sure. If you actually do it. The problem with most companies, and the reason why I think that TDD 'works' is that it forces you to right some tests, any tests at all. Many people, if they don't write the test first, will just end up not making any tests at all.
- fran7118 9y agoExactly. I worked at a place where the majority of dev teams had little to no automated tests. Getting them to write tests was near impossible due to it 'taking too much time away from features and bug fixes'. One team adopted TDD and forced themselves to do it for a year. They admitted it slowed their overall dev-cycle down a ton, but they ended up learning a lot about how to build a well designed API. I think more internal documentation about good design emerged from that then them evangelizing TDD.
- Spivak 9y agoIf that's really the case then why not drop the ceremony and simply TTD to, "write some tests first."
- Retra 9y agoCeremony sells!
- raverbashing 9y agoYes a lot of people kept parroting TDD (as most people parrot the latest fad without thinking for 3 seconds if they should actually do that) > I absolutely think _tests_ are useful, but have never found any advantages to test-DRIVEN-development (test-first). Seconded. Especially the crap about "make the test fail first" (making the test fail is useful, especially if you're unfamiliar with some aspect of it), but it should not be an obligation
- rsyntax 9y agoYeah I dislike that mantra, but I think it was a guideline for novice. I think the phrase should be updated to "make the test fallible".
- UK-AL 9y agoThe term making the test fail, isn't about explicitly making the test fail. It's about the fact that it's not implemented yet, so of course it going to fail!
- ehsanu1 9y ago> Especially the crap about "make the test fail first" (making the test fail is useful, especially if you're unfamiliar with some aspect of it), but it should not be an obligation Sometimes I write code to fix a bug, then a test the tests my bugfix. Test passes. I'm done right? Well, I make sure my test fails without the bugfix. About half the time, the test still passes. ie I wasn't testing what I thought I was testing. That's why you make the test fail.
- raverbashing 9y agoYes, that's why I say "making the test fail is useful" ;) And yes, I've seen this happen as well, either you're not testing the bug-causing issue or you're using a "Magical Mock Library" where x.does_thing() just returns true and you thought it did the thing but it didn't.
- hinkley 9y agoI was quite comfortable with refactoring for a very long time before I started doing tests, and I think that colors my approach to the problem space. I write code until it misbehaves the first time, and then I start writing tests, comfortable in the knowledge that in another half hour this code will be unit testable and look better than what everyone else is turning in, and that I'll spend 20 minutes at the end cleaning it up even more. But I am not everybody, and trying to set team policy on my comfort levels is quite likely a bit dangerous. I've paired with a few smart people this month and was horrified by how they refactor. Most trust their hands more than the tools. They do a LOT of transcription instead of copy and paste, and don't use the IDE's refactoring tools at all. That's not just inviting human error to the table, that's giving it the seat of honor. For most processes there are ways to do it that are worse than nothing at all, and unfortunately many of us seem drawn to them almost instinctively. I try not to think about it because it just makes me angry that the distance between what we collectively know and what we individually do is so wide.
- thaumasiotes 9y ago> They do a LOT of transcription instead of copy and paste Transcription makes you think about what you're typing in and avoids the whole class of errors that arise from copy-and-paste. Transcribing code rather than pasting it is not a red flag.
- hinkley 9y agoTranscription can lead to the same conditional in two blocks of similar code instead of the two intended. It can lead to fencepost errors due to changing or inverting an inequality. And in some languages it can lead to code that looks up data that doesn't exist (misspell a property name outside the happy path). The big problem is that you're chewing up short term memory slots that used to hold the next thing you planned to do. For me I generally use only a couple slots for a single refactor because it's all muscle memory. Additionally, it just takes a hell of a long time, which means you go from virtually no chance of interruption to a measurable one. When I was young I had an exceptional short term memory and could pull off some crazy big refactor without introducing errors. But now it's normal at best, and also I rely far less on those sorts of feats, in part because I put a higher value on commit history readability.
- smokeyj 9y agoI feel this way about code coverage (need that 100% badge). If I took on a project with low quality and had a limited budget to ensure QA - I'd prioritize E2E testing over unit testing.
- nickpsecurity 9y agoHere's you some evidence to use when you bring that up: https://blog.regehr.org/archives/872 https://blog.regehr.org/archives/872
- logfromblammo 9y agoAnecdotally, the cleanest, most maintainable code I have ever seen at work was the result of a TDD-mandatory project. Equally anecdotally, that was the most efficient project management I have ever seen at my workplace. Economically, our branch office was shuttered, and all employees laid off, just a few months after we delivered it, for almost entirely unrelated reasons. As it turns out, the only reason we did the TDD project at all is because our primary contract had not been renewed, and it was a lower-value contract. After it was done, we didn't have enough work left to do. But more importantly, the main office didn't have enough work to do, so they slurped up our dregs, kicked us to the curb, and sold themselves to a bigger company. Perhaps the lead dev saw the writing on the wall, and wanted to make a showpiece to boost everyone's resumes. Perhaps he foresaw that one of the several companies we might end up with after the diaspora might get maintenance responsibilities, and that person shouldn't hate their former team for writing crappy, unmaintainable code. Doesn't matter. Did it once, and it turned out great. I still get nostalgic for it sometimes, when I am working in the sloppy mess I am currently assigned to.
- aslakhellesoy 9y agoYou and I have very different opinions on the purpose of TDD. You're presuming it's a design technique - many do. For me, TDD is all about validating a design in a way that gives me faster and better feedback than if I were just coding. I design the code before I write the first test. In fact, my test is derived from the design. First I sketch a rough implementation design on a napkin or whiteboard. I don't worry about making it perfect, just good enough to create an initial mental model, and perhaps discuss it with someone. Then, I write a test to express how I would like to interact with a small area of an implementation of this design, typically by calling a function or method and expressing a desired outcome. I use the feedback from the test to drive out some simple code that resembles the design I sketched. If at any point this becomes hard, I take that as a clue that my napkin design can be improved, so I go back and tweak it. Then I start over again. This is a quick cycle, typically 10 minutes or so. I get constant feedback on my design as well as my implementation. I don't design code with TDD. I design code exactly the same way people who don't to TDD do it. Through intuition and experience. This is a skill, not a process. I only write tests to validate my design (not my implementation). Of course, because they are tests, they automatically validate my code as well, but that is not why I am doing it. If I only cared about testing, I might as well write them last. More often than not my designs are imperfect, but by doing TDD I always improve them significantly, with relatively little time and effort. It's taken me 15 years to get decent at it, and I'm still learning new techniques and abandoning others. There are many tricks to be learned. I usually only TDD when I care about the design. Quite often I don't, and then I just hack. I'm simply making a choice between going very fast now or going reasonably fast in a year. I haven't found a way to do both yet.