[HN Gopher] An unexpected benefit of unit tests
       ___________________________________________________________________
        
       An unexpected benefit of unit tests
        
       Author : matthewfcarlson
       Score  : 121 points
       Date   : 2023-01-21 16:26 UTC (6 hours ago)
        
 (HTM) web link (matthewc.dev)
 (TXT) w3m dump (matthewc.dev)
        
       | okamiueru wrote:
       | The title of this post doesn't do the author any favours. It can
       | be understood to be directed at the general reader, and I can
       | assure you, that what is described is very much an expected
       | benefit of unit tests.
       | 
       | But, it's also just a tool. That has to be used for the right
       | reasons and the right circumstances.
       | 
       | One unexpected use for them is to implement the "right way" to do
       | something as a unit test. Instead of having to remind them of
       | what we agreed on, making a test is a great investment, and not
       | the typical use case for tests.
        
       | trevor-e wrote:
       | Early on in my career I was overly enthusiastic about writing
       | unit tests for maximum coverage. I wouldn't say they were totally
       | useless, but anecdotally I don't think they caught many
       | bugs/regressions and over time it was a lot of code to maintain.
       | While it made refactoring feel 'safe' to do, it also slowed me
       | down quite a bit.
       | 
       | Now, my strategy is to instead focus on writing a few solid end-
       | to-end/integration tests. These tests often find just as many
       | bugs/regressions, are actually testing the entire system, and
       | much easier to maintain. Most bugs happen due to bad interactions
       | between systems. I still write unit tests for some tricky code,
       | it just isn't my first choice.
        
         | john-tells-all wrote:
         | After many years of experience, I agree.
         | 
         | Tests are a _business_ investment, of tech resources, to create
         | _business_ value.
         | 
         | It's good to focus on the Testing Pyramid [1]. High level tests
         | are slow and brittle, but connect the low-level code to
         | business features. Unit tests are fast and detail oriented.
         | 
         | In practice I write 1-2 high level tests (generally end to end,
         | sometimes UI, or API/integration tests) to help focus
         | development and have something that the business understands.
         | Unit tests are helpful to "smooth the path forward" to ensure
         | the code works as expected. Integration tests are great to
         | iterate on, so that new code and tests actually work with real
         | APIs correctly.
         | 
         | Tests are not free. However they create a _lot_ of value --
         | they create (business and tech) _confidence_ that the system is
         | working as expected. Like you mentioned they assist
         | refactoring, which makes the code much cheaper and easier to
         | work with.
         | 
         | [1] https://martinfowler.com/articles/practical-test-
         | pyramid.htm...
         | 
         | (disclaimer: writing a book about tech feedback loops, e.g.
         | tests)
        
         | nonethewiser wrote:
         | Uncovered code tells you something useful. That there are no
         | assertions made about some code.
         | 
         | Covered code tells you nothing. It tells you this code may or
         | may not have assertions made about it.
         | 
         | Tracking coverage is good because it shows you what code isnt
         | tested. But once its covered, you have no idea. So instead of
         | increasing coverage, you should be evaluating the new
         | assertions being made to uncovered code. But virtually
         | everywhere I've been has just used coverage going up to mean
         | your tests are sufficient.
        
         | roblh wrote:
         | What do you use primarily for writing integration/end-to-end
         | tests? Do you have any specific strategies for it? I've been
         | using cypress but not having a ton of success with it, it just
         | feels like everything is permanently broken, and
         | generating/maintaining mock data is exhausting.
        
       | sorokod wrote:
       | While unit test are important for various reasons "The broken
       | unit test stuck out like a sore thumb." is not one of them.
       | 
       | In the precise place in code you want to resume work, add in free
       | text whatever mental bookmark you need, the failing compilation
       | will stick out well enough.
        
       | makstaks wrote:
       | I wish HN had a central thread feature for discussing oft-debated
       | topics like TDD, microservices, and agile. This way, we could
       | contribute to the conversation instead of constantly rehashing
       | the same points through new posts.
        
       | btbuildem wrote:
       | > I've always poo-pood the idea of test-driven development (TDD),
       | as it seems wild to me to write a test for something that you
       | don't know what it needs to do yet
       | 
       | Right. If you don't know what it needs to do, why in the HECK
       | would you be writing any code at this point??
       | 
       | If you're just hacking / prototyping, there's no conceivable
       | reason to involve tests.
        
       | [deleted]
        
       | Waterluvian wrote:
       | Terms like "TDD" come loaded with all kinds of varying opinions
       | on what that means and how it should direct your other
       | behaviours. It's okay to write unit tests first without ever
       | thinking or saying "TDD."
       | 
       | "But that's TDD!"
       | 
       | "I dunno. I just wrote my tests first."
       | 
       | "But you're doing TDD wrong because you didn't go and make it
       | minimally pass before improving it!"
       | 
       | "I dunno. I just wrote my tests first."
       | 
       | Doesn't mean it's not right to adopt a pattern when you identify
       | that you're implementing something similar to it. But you aren't
       | obligated to get trapped in its gravity well.
        
         | drewcoo wrote:
         | > all kinds of varying opinions on what [TDD] means
         | 
         | I guess I go by what Kent Beck, who coined the term said TDD
         | meant.
         | 
         | https://www.goodreads.com/book/show/387190.Test_Driven_Devel...
        
       | [deleted]
        
       | UK-Al05 wrote:
       | Says TDD is silly. Then literally describes text book TDD as his
       | preferred approach?
       | 
       | Writing all tests up front has never been TDD.
        
         | matthewfcarlson wrote:
         | I'll admit, it was a fundamental flaw in my understanding. The
         | comment section here has given me a few things to think about
         | and I'll likely add an editors note onto the original article.
        
       | TillE wrote:
       | It's hardly a secret, but it's still not widely understood that
       | one huge advantage of starting with tests is that it forces you
       | to think about how an API will be used.
       | 
       | Instead of just writing a class with a bunch of methods that seem
       | useful, you write the tests to figure out what a user actually
       | needs, and iterate on that. And as a wonderful byproduct, you get
       | a suite of regression tests so you can comfortably refactor and
       | add features in the future.
        
         | marcosdumay wrote:
         | I'm willing to accept that there exists somewhere a large mass
         | of developers that don't think about how their code will be
         | used before writing it.
         | 
         | But the one thing I can't agree on is that teaching them TDD is
         | either necessary or sufficient for them to start thinking about
         | that. I'm pretty sure that if you can manage to write code
         | without knowing how it will be used, you will write tests that
         | way too. Writing a test doesn't force you to think about your
         | API any more than writing the API.
        
           | jdlshore wrote:
           | It "prompts" you to think about the API is probably a better
           | way of putting it. That prompt is helpful.
        
         | nonethewiser wrote:
         | How often do you write methods that you dont use? I only write
         | a method if I'm going to use it. In which case testing doesnt
         | offer that advantage.
        
         | drewcoo wrote:
         | > starting with tests is that it forces you to think about how
         | an API will be used
         | 
         | And tests that describe how an API can be called are BDD. Hold
         | the Cucumber.
        
         | tgv wrote:
         | Depends on what you're doing. If you're adding functionality
         | because of a need, you've got a minimal "API" (or rather,
         | interface) already defined. If you ignore that, you might end
         | up with an interface that's not complete and with features that
         | won't be used, but need to be maintained anyway.
         | 
         | There's no fast and hard rule, I'm afraid.
        
         | karmakaze wrote:
         | I often implement top-down, calling non-existent methods that
         | do what I wished they existed to do. Then implement the called
         | functions or even classes. The source should be comprehensible
         | on its own at each level with good naming and separation of
         | concerns. This is a generalization of how to write a recursive
         | function, pretend it exists before you write it.
        
           | lelanthran wrote:
           | I'm actually in the middle of a hobby project, where I am
           | keeping a devlog.
           | 
           | I've repeatedly, throughout my devlog, emphasised that I'm
           | calling functions that don't exist, then I create stubs of
           | those functions (a one liner returning an error), then I
           | populate the stub with actual logic.
           | 
           | To me, this is the most natural way of writing code.
        
           | mirekrusin wrote:
           | I write code bottom up. Break problem to first principles,
           | write/reuse primitive, combine them. Fixing bugs is easy as
           | one of underlying things doesn't work and you can scope it
           | down quickly and patch with test.
        
             | lelanthran wrote:
             | The problem with that is that _no one_ , not even you, has
             | 100% accurate foresight.
             | 
             | IOW, you will _never_ have your bottom layer done correctly
             | or completely.
             | 
             | At some top layer you're going to think "well, looks like I
             | won't be need that function", OR, "Well, looks like I am
             | missing a function for $FOO"/
        
               | garethrowlands wrote:
               | It doesn't have to be perfect to be a decent approach
               | sometimes. A spare or missing function isn't necessarily
               | that bad.
               | 
               | It's the premise of On Lisp, for example,
               | http://www.paulgraham.com/onlisp.html
        
               | Jtsummers wrote:
               | That's what refactoring/rewriting is for. You have your
               | piece-parts, you start combining them at the next layer
               | up. Realize some of them aren't what you need. Maybe you
               | need more, maybe they should be combined in some fashion,
               | or maybe their behavior should change. So you do. It's
               | not like people (well, fools might, but who wants to be a
               | fool?) say, "You know, I already finished that bottom
               | layer, I should never, ever, ever change it. That would
               | be admitting I was wrong and not psychic. I can't have
               | that." Since most people aren't fools they will, instead,
               | say something like, "Oops, that doesn't compose well.
               | Time to fix it."
        
               | mirekrusin wrote:
               | Bottom layers have functionality that is needed. There is
               | also reuse, dovetailing happens, you have opportunities
               | to see new problems from lenses of already solved ones.
               | 
               | We're using this approach in trading production system
               | for about 5 years now and I recommend it, works very
               | well.
               | 
               | There is also side effect where your dependency tree is
               | more shallow.
        
             | karmakaze wrote:
             | When it comes to interface design, I draw top & down to
             | middle. Find what interface or mechanism that is the common
             | thing that makes the lower-level agnostic and the upper-
             | level about applying or reapplying that mechanism in
             | varying contexts.
             | 
             | Some might call that thing a 'narrow waist' but I don't
             | quite equate it to that although it's a good example. Is
             | the thing you want a stream of bytes, sequence of messages,
             | unsequenced messages, or priority-ordered messages?
             | 
             | The problem with bottom-up is that you don't know the
             | context. The problem with top-down is that you do know the
             | context--and can leak it into what should be without. I
             | find the latter works out better if you're aware of
             | avoiding it. How do you avoid not having context without
             | going top-down?
             | 
             | An analogy in UX would be Windows vs Mac. Windows builds
             | things then puts the UI on what they've built--in many
             | different kinds of settings places. Mac figures out what
             | information & controls the user should have and how to name
             | and group them.
             | 
             | Edit: I actually have a recent real-world example. Making a
             | subsystem for querying and mutating things, what they are
             | doesn't matter. We set out making an 'adjust' operation and
             | a 'move' operation because we wanted to preserve paired
             | decrement/increment amount for moves. We were also certain
             | that we only needed to move between two things, or adjust
             | many things but only of one kind. We got all the way up to
             | the top where the public API was getting close to complete.
             | We discovered use-cases that made sense to to more than
             | those operations. We were able to shuffle things around and
             | ended up with a fully-capable 'adjust' operation that can
             | work on any number of kinds/things, and a fully-capable
             | 'move' operation that can work on any number of
             | kinds/things. On top of that we added checks to only do
             | what we need now. The more general things were
             | substantially harder to make work efficiently which is why
             | we chose not to do it when we were sure we didn't need it.
             | We were wrong.
        
               | mirekrusin wrote:
               | I don't get what you're trying to say.
               | 
               | You know the context. You have the whole problem in front
               | of you. The question is about which direction you're
               | going to solve it.
               | 
               | For "you want a stream of bytes, sequence of messages,
               | unsequenced messages, or priority-ordered messages?" the
               | answer is you probably want type parametrized iterator at
               | this level if you can ask this kind of question.
               | 
               | To put it in other words you put more attention into
               | trying to find underlying composition of algebras in
               | problems than doing adhoc, inlined imperative
               | constructions littered with if statements that you add
               | every time some issue is discovered.
        
           | garethrowlands wrote:
           | This is sometimes called the London school of test driven
           | development. At least, if you then mock the non-existent
           | methods to make your top level test pass.
        
         | taeric wrote:
         | I like what you are saying, but I've seen enough test driven
         | development to know that it doesn't force any thinking about
         | how the API will be used. It certainly can help illuminate
         | that, but I've been constantly surprised by the convoluted "use
         | case" tests that I've seen people create and think nothing of
         | it.
        
         | njharman wrote:
         | I learned TDD as a development method. Too many people imagine
         | TDD is about testing.
         | 
         | It's explained right there on the cover Test *Driven*
         | Development. Not a testing method. A development process guided
         | by unit "tests". In quotes as unit tests are at least 51% about
         | forcing you to write small, isolated blocks of code with well
         | defined and simple interfaces. Units. A large percentage of
         | remainder is enabling ruthless refactoring (refactoring being a
         | huge part of TDD's development practice/philosophy) by ensuring
         | you have not violated those interfaces. Only a few percent is
         | actually about having "correct" code.
        
           | garethrowlands wrote:
           | This is one motivation for BDD - it doesn't contain 'test' in
           | the title, so it's easier to explain that it's a development
           | method.
        
       | logicallee wrote:
       | One reason for unit tests is to know when you've won. For example
       | ChatGPT is already a limited form of general AI, but makes some
       | silly mistakes for certain kinds of thinking.
       | 
       | An example would be - oh wait, it just passed it, nevermind. (I
       | wanted to quote a kind of numerical thinking test it fails at it
       | but it just passed.)
       | 
       | If this had been a test previous GPT's failed at, but it was
       | coded up in a unit test, then maybe more people would get it. At
       | some point AI will just pass all the tests we throw at it. It
       | would be nice to have a spreadsheet of tests to 1) know whether
       | we're there 2) show people that we're there.
        
       | RHSeeger wrote:
       | I tend to describe programming as building a house of cards in
       | your mind. It's a way of thinking about it that's used sometimes
       | to explain why interrupting a developer can be so frustrating
       | (because they have to rebuild the house).
       | 
       | Writing code in pieces (functions) where each does one thing,
       | even if that thing isn't reused elsewhere outside of the one
       | place you need it, if one way to reduce the impact of that house
       | of cards; because you only need to focus on the logic that's
       | important "where you are".
       | 
       | Unit tests are another way. You can describe the behavior of your
       | code in tests and, if you break something because you couldn't
       | keep the entire model (and individual pieces) in your head at the
       | same time, your tests help you notice that. If you're building
       | your code initially, your tests can help you identify and focus
       | on individual behaviors you need to work.
       | 
       | I think the above is why I think both pre-code and post-code
       | tests are useful. It's helpful to write tests before you write
       | the code, to guide your development. But it can also be helpful
       | to write tests after; especially as you identify things that
       | don't work correctly because you didn't realize they were
       | requirements when the code was originally written. They are,
       | effectively, regression tests... but they're more than that, too.
        
       | iraliaf wrote:
       | I just started working at a 15-person startup where I am on a
       | small team of 5 devs. People typically "don't have time to write
       | tests" and are encouraged to pump out new shiny features every 2
       | weeks.
       | 
       | I am a new to development but I've noticed that lots of errors
       | pump through code where "No method X defined for nil object" or
       | something like that. I wonder if I could somehow make the case
       | that we'd spend less time reacting to problems and bugs if we
       | spent more time up front writing tests that could give us more
       | than 19% code coverage.
       | 
       | Does anyone have advice for starting this conversation with my
       | boss or during one of our standups? I know I could somehow pitch
       | the value as "less problems later for more time spent now" but is
       | there a more effective way to say it?
        
         | golemiprague wrote:
         | [dead]
        
         | vanjajaja1 wrote:
         | Given that you're new to development, you'll probably lack the
         | sway to get people onboard with just words. It's likely that
         | the team already knows about testing and the benefits of it
         | (its not a secret, the text is everywhere,) and yet they choose
         | to not do it still. They believe tests will slow them down,
         | probably based on past experience, and using the words of
         | people who are not there on the front lines is unlikely to sway
         | them.
         | 
         | To get out of that situation, I'd recommend leading by example
         | and showing how you have personally managed to use tests, in
         | the space they are operating in, to make your life better and
         | develop faster. Once you have something that has provably
         | worked for you, you can start evangelizing it and onboarding
         | other people.
        
         | karussell wrote:
         | You could introduce (or write more) tests from your side for
         | new feature that you write or for bugs that appear. But in
         | general it takes a bit more experience to get unit testing into
         | existing code, but not undoable and you could practise this for
         | a smaller, independent or even hobby project. Still, the
         | critical part is to get support, not just acceptance, from your
         | boss as sometimes "less problems later" argument does not
         | convince them as "it always worked well without tests".
         | 
         | And just adding tests isn't the only thing it takes to improve
         | the software quality.
        
       | revskill wrote:
       | For anyone who hates to write tests, there's another testing
       | strategy that i also found practical and useful, it's called
       | Snapshot Testing. Basically, you can write a script to dump all
       | outputs. And everytime you change the code, just try to compare
       | the old vs new snapshot output.
        
         | candiddevmike wrote:
         | Wonder if you could do this with some kind of deterministic
         | fuzzing
        
           | revskill wrote:
           | Hm, it's new to me. Do you have any guide for this technique
           | ?
        
       | thadt wrote:
       | I like it.
       | 
       | I appreciate someone writing into the void how this helps them
       | focus and be productive in their workflow. Who cares whether it
       | 'fits the technical semantic definition of INSERT_ACRONYM_HERE'.
       | 
       | Unit tests are a literal investment - somewhat like buying
       | insurance. Sometimes they're really great - almost necessary -
       | and other times they're not worth the price. The article is
       | offering a point of view of how tests are paying for themselves
       | in an indirect way, which is useful to think about in the cost
       | benefit equation.
       | 
       | The position of "insurance is useless" means maybe you just
       | haven't been in a situation where it could really, really save
       | your bacon. Likewise, if your rule of thumb is "buy all the
       | insurance, all the time" - then BestBuy will gladly sell you a $5
       | 'product protection plan' on your $10 purchase.
        
         | matthewfcarlson wrote:
         | Yes, you're getting it exactly. This was for a personal
         | project. The expected audience for it was my spouse and I and
         | writing tests seemed like a nuisance. I don't even know why I
         | started as getting the environment building in a testing
         | harness was actually quite a bit of work. I noticed a benefit I
         | didn't expect and wanted to share.
         | 
         | Did I not have a concrete idea of what TDD was when I wrote it?
         | Yes. I had muddled test first development with test driven
         | development as the two people I knew who were big on TDD were
         | focused on TFD as they were refactoring large legacy codebases.
         | I'm still murky on what TDD is (and it sounds like opinions
         | differ) but I don't think I've discovered anything new. Just
         | new to me.
        
       | epolanski wrote:
       | I like TDD myself but I'd rather write E2Es or integration tests.
        
         | Jtsummers wrote:
         | Why not both? That's what I do, it's very effective and nothing
         | about one prevents the other.
        
       | mcculley wrote:
       | It is said that Hemingway leveraged the same effect by leaving a
       | sentence unfinished when stopping writing for the day. I use the
       | same technique to remind myself I am not finished with something.
       | This is also called the Zeigarnik effect:
       | https://en.wikipedia.org/wiki/Zeigarnik_effect
        
         | bbx wrote:
         | I knew about the Hemingway method but didn't know there was a
         | psychological name for that effect.
         | 
         | It also goes well with the pomodoro technique, because that
         | almost forces you to interrupt your progress while in the
         | middle of something, leaving things unfinished, so they end up
         | easier to pick up later.
        
         | joshcanhelp wrote:
         | Came here to post this.
         | 
         | > The best way is always to stop when you are going good and
         | when you know what will happen next. If you do that every day
         | ... you will never be stuck.
        
       | aatd86 wrote:
       | I will be a little contrarian maybe but I don't really start with
       | tests.
       | 
       | The rationale being that until the API is fixed, I don't want to
       | have to adapt the tests to the changing API all the time.
        
         | bobleeswagger wrote:
         | I don't get the obsession with TDD. Maybe it's because I have
         | been in the BI/DE space for awhile, but nothing is defined well
         | enough to justify TDD in the first place. Everything is hobbled
         | together and functional integration tests are much more
         | important. Unit tests aren't going to catch when my CF stacks
         | are broken. Good dev tools and architectural design are much
         | more important in my experience.
         | 
         | I totally understand where TDD shines. When you have a well
         | defined problem to solve, unit tests are the definition itself,
         | and the business logic therein.
        
       | AndyPatterson wrote:
       | I think the sincerity of this makes this the funniest thing I've
       | read on HN.
        
         | drewcoo wrote:
         | I like the way that the author literally reinvents TDD while
         | badmouthing "TDD." Which garners lots of commenters not talking
         | about the author's content, but just here to also badmouth TDD.
         | 
         | It's possibly the best testing-related Poe/trolling I've seen.
        
           | matthewfcarlson wrote:
           | Yes, this comment thread has correctly pointed out that my
           | understanding of TDD was and still is incomplete. I think
           | there's some aspects of TDD (such as refactoring after
           | implementation) that I'm not interested in. The two folks I
           | know who are big TDD proponents tend to gravitate towards
           | test first development (TFD). In my mind, that was TDD. You
           | write the tests and that drives how you develop the tests.
           | 
           | In a recent personal project I started using tests on a bit
           | of a whim and found an unexpected benefit that I wanted to
           | share.
        
       | oweiler wrote:
       | You can also leave the code you've worked on last broken.
        
       | valenterry wrote:
       | I don't like the dogmatic test driven development appraoch - so
       | here's mine:
       | 
       | ---------------------
       | 
       | 1. Use a programming language with an expressive statical type-
       | system
       | 
       | 2. Write only type-signatures for everything that I want to do
       | (using type-holes) and continously compile my code
       | 
       | 3. Once I'm finished with #2 and it all compiles, I start
       | implementing all the methods/functions
       | 
       | 4. If I can't implement a method because I got the types wrong
       | (e.g. my types say that I will return a number, but in some cases
       | I cannot and have to return nothing or an error) then I change
       | the type-signature and go back to #2
       | 
       | 5. If I can implement a method but I'm not sure it will do the
       | right thing (despite compiling) without running it manually to
       | see if it works, then I write one or more unit tests for the
       | method. If I believe that changing the method will easily break
       | it I also write a test. Otherwise I write no tests.
       | 
       | 6. Once finished I write some integration tests for what I deem
       | necessary - both positive as well as negative tests.
       | 
       | 7. Done
        
         | garethrowlands wrote:
         | Sometimes, I find I need to revisit some type signatures after
         | I've begun to write code. And this can lead to my code not type
         | checking for longer than I'd like. But I can still run my tests
         | if I defer type errors to runtime (they still appear at compile
         | time but as warnings). See (Deferring Type Errors to Runtime)[h
         | ttps://downloads.haskell.org/~ghc/9.4.4/docs/users_guide/ex...]
        
       | Learner100 wrote:
       | > 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.
        
         | nine_k wrote:
         | Two-step TDD can work if you implement a well-known, stable
         | spec, say, an IP stack.
         | 
         | Such cases are relatively few.
        
           | jdlshore wrote:
           | There's no such thing as "two-step TDD." That's a cargo cult.
           | 
           | Real TDD is red/green/refactor/repeat in very small steps,
           | writing a handful of lines of code at each step.
        
             | nine_k wrote:
             | I see; I should have quoted it. I was referring to the
             | expression used in the grandparent post.
        
           | DougMerritt wrote:
           | Right, 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
        
             | Jtsummers wrote:
             | Hardly, 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 wrote:
               | I 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 wrote:
               | But 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.
        
               | DougMerritt wrote:
               | Fair enough.
               | 
               | For the moment I will assume that I have a misconception
               | that led me astray, and that I should correct in the
               | future once I double check the thinking and the
               | definitions etc., and I'll upvote you now for leading me
               | in a better direction.
        
         | JoeNr76 wrote:
         | Even after all these years, people still have this
         | misconception about TTD. No idea where it came from.
        
           | jdlshore wrote:
           | Microsoft. It came from Microsoft.
           | 
           | https://www.jamesshore.com/v2/blog/2005/microsoft-gets-
           | tdd-c...
           | 
           | (Well, they popularized it.)
        
           | nonethewiser wrote:
           | Dogmatic evangalizers
        
         | sedatk wrote:
         | TDD 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 wrote:
           | I 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 wrote:
           | TDD'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 wrote:
             | Still, you write _a test_ first which, again, the author
             | diverged from.
        
         | lowbloodsugar wrote:
         | It 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
        
         | Jtsummers wrote:
         | I 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.
        
           | QuercusMax wrote:
           | I don't know why you'd care about a brand new test being
           | broken overnight. It's not like you're submitting the change
           | yet anyway. It's just leaving a breadcrumb.
        
             | uldo wrote:
             | I think it is a good practice to push only working code, I
             | think it stems from this. And it has a good point - you
             | have finished something. Sometimes if you leave something
             | "hanging" you keep thinking about it for a while,
             | especially when falling asleep.
        
           | deckard1 wrote:
           | > (page unknown, ebook copy, chapter 27)
           | 
           | page 148 in the physical book, at least my copy.
           | 
           | However, you leave out the following section titled "Clean
           | Check-in" in which Beck argues the exact opposite when
           | working on a team. Clearly it's the days before git and
           | trusting code to sit on your PC overnight without checking it
           | in (I, too, would not trust Windows98 with code that long).
           | 
           | But this is the general problem with the book and TDD. It's
           | outdated and overrated. The book is not a well-written book,
           | even for year 2000 standards, and I'm quite surprised people
           | are still referencing a 20 year old book that is almost
           | entirely composed of trivial examples using Java classes and
           | objects. It is my least favorite tech book on my shelf.
           | 
           | > leaving a failing test at the end of the day
           | 
           | As far as this point goes, you pretty much have to. The TDD
           | methodology, per the book, is to get to green as fast as
           | possible. If you are testing for a function to have inputs of
           | 5 and 2 and expect an output of 10 then the book literally
           | tells you to do:                 function() {         return
           | 5 * 2;       }
           | 
           | What's going to happen is you write that code, get distracted
           | or need to quit for the day, and you come back and your test
           | is green. You forget that you wrote some total shit like the
           | above and move on to the next Jira ticket.
           | 
           | As a methodology or system it's just bad. Imagine instead of
           | writing the implementation to pass the tests that you instead
           | are writing tests for some AI to "implement". You set the
           | inputs and the expected outputs and the AI goes to work and
           | does the implementation. In AI this would be called
           | overfitting. Yes, the tests pass. But only for the cases you
           | wrote. There is no guarantee your code works for the general
           | case. Now replace AI with you and the same thing will happen.
           | If all you care about is green tests you're liable to stop
           | thinking of _what_ the implementation should be doing.
           | 
           | Sure, _you_ won 't do that. But I promise you people, in
           | general, do. One code base I worked on had minimum coverage
           | required. More than half of the tests were total garbage.
           | They either tested nothing useful, or were false positives
           | that could never fail (because no one knows to check for
           | failure, ever!). This is the opposite scenario, but the same
           | outcome. It's the difference between the letter of the law
           | and the spirit of the law. Give the people a rule to follow
           | and they will mindlessly follow it.
           | 
           | Agile and TDD are both too nuanced and leave too much up for
           | interpretation that it's no surprise we continually see
           | debate on "true TDD" or "true Agile".
           | 
           | There are useful ideas in TDD. But this would be better
           | packaged and sold as "here are some ways to build software
           | under X scenario." These are techniques applicable to a time
           | and place and not a paradigm.
        
             | Jtsummers wrote:
             | Yes, I was actually considering writing another comment
             | about the next section and how that's (somewhat) changed
             | thanks to git and better distributed version control
             | systems these days.
             | 
             | Regarding that code snippet:
             | 
             | If you forgot that you hadn't finished it, and then check
             | it in, then that's on you. Good news, hopefully you and
             | your office aren't morons and you aren't relying _just_ on
             | the tests from the TDD bits, because TDD itself doesn 't
             | directly address creating integration and end to end tests.
             | So your integration tests will catch that. And if not,
             | it'll make it to production and your customers will,
             | rightly, call you a moron. And you'll be embarrassed, write
             | a test to catch the error, fix it, and hopefully not fuck
             | up like that again.
             | 
             | TDD doesn't aim or claim to cover all the testing needed to
             | verify and validate a system. It's one part of the whole
             | (if you use it at all). In fact, it doesn't address
             | validation at all so that's something you have to cover
             | another way entirely.
             | 
             | > If all you care about is green tests you're liable to
             | stop thinking of _what_ the implementation should be doing.
             | 
             | If all you care about are green tests, I'd say you're
             | aren't just liable to stop thinking but that you _have_
             | stopped thinking. You have become, in my more polite way of
             | saying it these days, a fool. My advice: Don 't be a fool,
             | you have a brain, use it.
        
           | mrnonchalant wrote:
           | I like this a lot actually l
        
           | alasdair_ wrote:
           | Beck 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 wrote:
             | Having 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 wrote:
               | > corporate watering down of Agile concepts
               | 
               | I think _inverting_ agile concepts is more accurate.
        
               | Jtsummers wrote:
               | It'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.
        
               | Gibbon1 wrote:
               | Corporate agile looks from a distance like waterfall in a
               | dress if you get my drift.
        
               | evil-olive wrote:
               | I once interviewed at a place that told me they did
               | "design sprints", "coding sprints", "integration
               | sprints", and "testing sprints"
        
               | dccoolgai wrote:
               | Agile is a lot like Communism: there's a lot of people
               | who swear it works great but everywhere it seems to have
               | been tried it hasn't gone well... And the apologists just
               | insist "those places didn't understand it and do it
               | right".
        
         | User23 wrote:
         | TDD 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.
        
         | brightball wrote:
         | Yep, 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.
        
         | lamontcg wrote:
         | I'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.
        
           | Jtsummers wrote:
           | An important element of TDD is that it wasn't supposed to be
           | prescriptive. It's a tool/technique to use. Sensible people
           | know when to use it and when not to. Even Beck (who many here
           | and elsewhere assume is dogmatic about TDD) has said on many
           | occasions he doesn't use it 100% of the time and wouldn't use
           | it 100% of the time.
        
             | hitchstory wrote:
             | Unfortunately Uncle Bob used to preach 100% usage and IIRC
             | Beck never really specified when it should be used.
        
               | Jtsummers wrote:
               | I mean, how can he? You're the one that knows your system
               | so you have to be the judge on whether TDD is appropriate
               | or not for it, or for parts of it. He says as much in his
               | talks on TDD and in his book. You have a brain, use it,
               | don't expect him to think for you on systems he has no
               | knowledge of.
        
             | [deleted]
        
             | nonethewiser wrote:
             | Does test driven mean you need to write the test first?
             | Seems pretty clear to me. I dont think testing needs to be
             | so dogmatic but TDD kinda is.
        
               | Jtsummers wrote:
               | That's not dogma, that's the technique. If you aren't
               | writing the test first then you aren't doing TDD, and
               | that's _fine_. There 's nothing wrong with not doing TDD
               | at all if that's what you choose.
               | 
               | The dogma most people see or claim to see is that TDD is
               | meant to be used _everywhere_ (or nearly). Which some
               | fools, yes, believe. But they 're just that, fools.
               | People who use their brains (aka, non-fools) know them to
               | be fools and do what works for them and the circumstances
               | because they spend some time thinking about things
               | instead of parroting a dogma (or an anti-dogma).
        
               | bcrosby95 wrote:
               | Dogma tends to be a sign of an intermediate experienced
               | developer.
        
               | lamontcg wrote:
               | The "fools" are writing most of the blog posts on TDD.
               | 
               | At some point the No True Scotsman fallacy kicks in
               | pretty hard and that is just what TDD actually is.
        
               | bloat wrote:
               | If you're doing TDD then yes, you're writing the tests
               | first. But even Kent Beck says you don't need to do TDD
               | all the time. So where's the dogmatism?
        
           | Izkata wrote:
           | Pretty much the same for me: I'm only able to write any sort
           | of decent tests if there's some initial version of the code
           | already there. Typically that means a prototype-ish only-
           | works-on-the-happy-path version I wrote in an exploratory
           | way, then that first test can be for the happy path. After
           | that it's pretty easy to copy the test, tweak parameters, and
           | figure out where the first version breaks TDD-style.
        
         | danielvaughn wrote:
         | Also, 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 wrote:
           | I 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_.
        
         | beached_whale wrote:
         | Combine 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 wrote:
           | These are way, way more useful than unit tests IME.
        
         | tsuujin wrote:
         | There 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 wrote:
           | It 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.
        
           | tester756 wrote:
           | I don't see benefits of R->G step
        
             | nimih wrote:
             | I'm not sure about you, but in my experience, trying to
             | ship code while it still has failing tests tends to not get
             | approved during code review.
        
             | Jtsummers wrote:
             | What do you mean? The Red-Green step is "test fails, test
             | passes". You don't see the benefit of the test passing? Or
             | you don't see the benefit of having known that the code
             | _previously_ didn 't pass?
             | 
             | If you skip straight to green you don't know, for certain,
             | that the test actually tested what you expected. This isn't
             | even a TDD thing. When you're working on an existing,
             | deployed, system and a user finds a problem, you generate a
             | new test (well, sensible orgs and people will). That test
             | _will_ fail, because you haven 't addressed the issue yet.
             | That is, it's "red". Then you make it pass by fixing the
             | system, it becomes "green". That's it. If you fix the
             | system and _then_ write the test, do you know that the test
             | _actually_ recreated the original failure? Or is it merely
             | exercising the new or altered code?
        
               | tester756 wrote:
               | When fixing bug it makes sense.
               | 
               | But when writing new code?
        
               | mpweiher wrote:
               | > [R->G] But when writing new code?
               | 
               | Absolutely, and most prominently when writing new code.
               | The red-green transition is absolutely _essential_ for
               | new code.
               | 
               | You should not write any production code except to make a
               | red test green.
               | 
               | Think of the tests as the specification of your system.
               | 
               | If all tests are green, your system meets the
               | specification. Thus there is no need to write production
               | code.
               | 
               | So in order to make the system do something new, you need
               | to first change the specification. So you add a test.
               | When you add this test, it will almost certainly fail.
               | After all, you haven't written the code to implement the
               | feature.
               | 
               | Then you make the test green, and now the system once
               | again matches the (now updated) specification.
               | Commit/Refactor/Commit.
               | 
               | Having the test that is red also validates your tests. If
               | your tests are always green, how do you know that you're
               | actually testing something?
               | 
               | In fact, it sometimes happens that you write a test that
               | you think should be red, because you haven't implemented
               | the feature yet, but then it starts of as green. Meaning
               | you inadvertently already built the feature. This can be
               | very confusing... :-)
        
               | lolinder wrote:
               | The red step is how you test your test: if you don't get
               | red at first, your test is wrong.
               | 
               | If I only see green for a given test, I have no way of
               | knowing if it is asserting anything at all, much less if
               | it's testing what I thought it was.
        
               | Jtsummers wrote:
               | It does make sense with new code, yes. Whether for all
               | code or not, or whether unit tests or integration or end
               | to end tests though is up to your judgement. No one but
               | you, who knows your system and your capabilities and
               | knowledge, can decide for you. Only fools expect a
               | technique to replace thinking. I assume you're not a
               | fool.
               | 
               | For new code, the reason it makes sense is that your
               | system _is_ bugged. It does not do what it 's intended to
               | do yet because you haven't written the code to do it (or
               | altered the existing code to add the new capability). So
               | the test detects the difference between the current state
               | of the system and the desired state, and then you
               | implement the code and now the test detects that you have
               | achieved your desired state (at least as far as the test
               | can detect, you could still have other issues).
        
               | lowbloodsugar wrote:
               | If you write the code first and then the test, how do you
               | know your test works? You've only ever run it against
               | working code.
               | 
               | Like if there was a virulent disease for which there was
               | a 100% cure, but you can't get the cure unless you test
               | positive for the disease. I give you a test and say "Yep,
               | test says you are healthy". Ok. What if the test _always_
               | says people are healthy?  "Have you ever tested an
               | unhealthy person and the test detected that they were
               | unhealthy?" "Oh, no, we've done this test a thousand
               | times and it always says people are healthy!"
               | 
               | You write the test first, because your code does not yet
               | have the feature that you are testing. Your current code
               | is a perfect test for the test. Anyone who has done TDD
               | for even a short amount of time has written a test that
               | _should have failed_ but instead it _passed_. Sometimes
               | the was just a simple error in the test. You fix the test
               | so it can detect what you are looking for (i.e. the test
               | now fails). Other times a fundamental misconception was
               | discovered that blows everyone 's mind.
        
               | garethrowlands wrote:
               | Yes, new code too. If the test doesn't fail at first, you
               | have a different problem than you thought.
        
               | jfengel wrote:
               | There is a saying that coding is debugging a blank piece
               | of paper. ("Piece of paper" tells you how old this saying
               | is.)
               | 
               | "New code" just means "I want a program to do a thing,
               | and it doesn't do it yet. That's a bug." The difference
               | between "bug" and "new feature" is more a matter of
               | perspective than actual development effort.
        
           | jpatt wrote:
           | One 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.
        
         | heywhatupboys wrote:
         | it 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.
        
         | sorokod wrote:
         | _It '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 wrote:
           | It'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 wrote:
             | TDD 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
        
       | adamwong246 wrote:
       | shameless plug of my side project:
       | https://github.com/adamwong246/testeranto
        
       | Pxtl wrote:
       | My rule is simple: _if_ I want to test something, I have to code
       | it. I don 't know if it's full TDD, but if it's worth testing
       | it's worth automating. If there's some edge case where I trust
       | the logic to Just Work, I don't test it at all.
       | 
       | The important thing I find that helps with the mental overhead of
       | testing is a long script of integration tests is better than
       | nothing. Too often "TDD" is interpreted as creating perfect
       | isolation of unit tests on each method -- which is fantastic! But
       | is also a lot of work.
       | 
       | Worse is better. If just checking in some precondition data and
       | loading it in and then sequentially running a script of automated
       | tests that all have side-effects that could impact the next test
       | is your MVP for a test framework, do that! Having a suite that
       | you have to run fully end-to-end isn't great, but it's better
       | than nothing.
       | 
       | When that frustrates you, refactor it into true isolated unit
       | test.
        
       | adrianmonk wrote:
       | > _TLD focuses on ping-ponging between test and code._
       | 
       | I like this same concept for top-down and bottom-up design.
       | 
       | Top down advocates might say if you focus on implementation
       | concerns, you risk building what's easy to build instead of
       | what's needed.
       | 
       | Bottom up advocates might say if you focus on what you'd like to
       | have, you risk building something that just can't be implemented
       | well when something else (that can) might have been fine instead.
       | 
       | I think, instead, you should ping pong between both. Start at the
       | top, think about the high-level design you want, then work
       | downward and see if it makes sense. Maybe even write some code to
       | try out some ideas. (Like, if I use this design, I'm going to end
       | up needing this database query. Can I make that run efficiently?)
       | Then percolate those lower-level concerns and the results of that
       | research upward and see what adjustments you can make at a top
       | level. Then repeat until you find something that works well at
       | all levels.
        
       | tonto wrote:
       | typescriptifying some legacy code is a similar idle brain task
       | that i've sometimes done
        
       | k__ wrote:
       | I always think, the next time with tests.
       | 
       | Then I have to integrate a monster of a library that needs a
       | whole battery of polyfills, and does it's own thing rendering
       | modals somewhere, and I'm not in the mood anymore.
       | 
       | Mocking that whole thing? Hmnothanks.
        
         | xiphias2 wrote:
         | I'm seeing the terrible practice of people writing code bases
         | where a lot of logic depends on the ui framework.
         | 
         | I guess your case is something like this as well, that's why
         | it's so hard to test.
        
           | k__ wrote:
           | True story.
           | 
           | Problem is, often you get an easy integration route, where
           | lib and UI come in one package or a hard route, where you
           | have to build a UI around the lib.
           | 
           | To save time, you use the full package, but then testing
           | becomes a nightmare.
           | 
           | And the sad thing is, the stuff that's hard to test is
           | crucial to test.
        
             | xiphias2 wrote:
             | Haha totally agree :)
             | 
             | I guess in this case what matters is the estimated lenght
             | of the product lifecycle.
             | 
             | I'm right now writing open source library in ts hoping that
             | a ui will pick it up. I may need to write the UI myself at
             | the end, but that will be much easier after I put every
             | feature in my library I can think about :)
        
         | TillE wrote:
         | Despite the niche obsession with 100% test coverage, tests
         | aren't an all-or-nothing thing.
         | 
         | Test what can easily be tested. Architect your code such that
         | business logic, etc is modular and testable. Don't worry too
         | much about testing the hard stuff, especially if it's not
         | likely to break.
        
           | k__ wrote:
           | But isn't hard stuff the most ugly one when it breaks without
           | a test?
           | 
           | Stuff that's easy to test is usually also easy to debug.
        
             | Izkata wrote:
             | IMO there are 3 main purposes for tests, and knowing which
             | one you're doing helps decide if a test is worthwhile, as
             | well as how in-depth the group of tests needs to be:
             | 
             | 1) Helping you work through a complicated piece of logic.
             | 
             | 2) Encoding some sort of requirement so future
             | refactoring/bugfixes/features - perhaps written by a new
             | developer - don't break that requirement.
             | 
             | 3) When fixing a bug, ensuring the bug doesn't reoccur.
             | 
             | Tests that fall under (2) often feel the most useless, but
             | I've found to be the most useful. They're typically the
             | simple ones that don't feel like they need a test, but
             | years down the line not every developer knows these
             | requirements. Documentation is easily missed or ignored,
             | but a test that's started failing? Sure there's still a
             | chance they'll just remove/change/skip the test, but they
             | can't just ignore or forget about it like with
             | documentation.
             | 
             | Tests that fall under (3) are very similar to (2), except
             | it's not an external requirement known from the start.
             | These are ones that I've seen people occasionally write
             | while they're fixing the bug, then remove afterwards so as
             | not to clutter up the tests. Or do manually in the shell
             | and never write a test in the first place (I'm definitely
             | guilty of that). But whatever happened here was just
             | complicated enough that the previous develop(ers) missed
             | the conditions that caused the bug - so future changes to
             | this part of the code have a good chance of reintroducing
             | it or something similar. These are worth keeping.
             | 
             | Tests that fall under (1) are definitely useful while the
             | code is being written, and typically people want to keep
             | these because the logic is complicated (or even only write
             | them because of the complicated logic, even if they didn't
             | need it to write the code), but I'd say there's a further
             | question here: How likely is this code to change (ever)? If
             | you didn't write tests with the initial code, it all works,
             | and it's something relatively generic that is unlikely to
             | change... it might not be worth it. If it's likely to
             | change it could end up falling under (2) or (3) in the
             | future, so it might be worth a detour in writing the tests.
             | If the tests already exist because you needed them for case
             | (1), then it shouldn't hurt to just not delete them.
             | 
             | (I'm sure there's other purposes that don't fall under
             | these three, but these are the main reasons for tests in my
             | mind)
        
       | jonathaneunice wrote:
       | Not quite binary ping-ponging, but I've have often found rotating
       | through writing functional code, writing documentation of what's
       | desired, and writing test cases very helpful in defining "What
       | should each function do? What parameters and context should each
       | take? How should the overall solution be structured?" in cases
       | where that isn't entirely clear (e.g. first pass at solving new
       | or complex problems).
       | 
       | Sketching functional code (and/or pseudocode/stub-only functions)
       | lays down a concrete hypothesis about what the code (or at least
       | its key parts) should be. Writing docs fleshes out the
       | aspirations and expectations (including helping you define non-
       | goals and not-yets). Writing tests demands thinking through edge,
       | corner, and other hard cases. Each informs one's understanding
       | and intuition about the other two, and almost automagically
       | drives gap analysis. "Whoops! Haven't thought
       | about/implemented/documented/tested that yet! Let's go do that
       | now! (or at least put it on the TODO list)" It's a strong tight
       | loop.
       | 
       | Would also suggest you don't limit your "write tests" phase/work
       | to pure "does just one thing" unit tests. You don't initially
       | want full end-to-end tests that assume and require everything's
       | working in order to start testing, but some of the tests can and
       | probably should venture into the "requires multiple components
       | interacting" space usually called "integration tests." I think of
       | that happy middle ground / hybrid between pure unit and full
       | integration tests as "functional tests" or "functional block
       | tests" where the size/complexity of the functional blocks under
       | test have more leeway than unit testing/TDD dogma usually allows.
        
       | major505 wrote:
       | Being honest most companies I worked, unit tests have no use at
       | all. They are just writen because someone determined some
       | arbitraty percentage of code that needs to be covered, and
       | because it takes as much time to write the tests as to write the
       | code, people just write useless tests that test absolutaly
       | nothing usefull, and are worth nothing.
       | 
       | And this is not small companies we are talking about. They are
       | banks, big educational institutes, etc. In the end the only thing
       | most managers care is that the litle label in the pipeline
       | dashboard that indicates the test percentage stais green.
        
         | videogreg93 wrote:
         | Thank you! I feel like all of this Unit test stuff is just a
         | case of Emperor's New Cloths: everyone writes tests, no one
         | knows why but won't admit it. I've never never ever experienced
         | in my life a time where a major problem was automatically found
         | by some unit tests before it got pushed to production.
        
           | UK-AL wrote:
           | I have. A lot.
        
             | cwilby wrote:
             | Our team too, time saved is fairly quantifiable.
        
               | disgruntledphd2 wrote:
               | I mean, you probably need automated CI before this
               | becomes likely as otherwise people just forget to run the
               | tests...
        
               | nonethewiser wrote:
               | You absolutely need that.
        
           | matthewfcarlson wrote:
           | This is sort of why I wrote the article. Professionally, I've
           | seen tests just cause tons of noise and offer little benefit.
           | So I tried applying it to a personal project on a whim and
           | found some unexpected benefits
        
           | jerf wrote:
           | "I've never never ever experienced in my life a time where a
           | major problem was automatically found by some unit tests
           | before it got pushed to production."
           | 
           | Then you, or somebody near you, is doing it _very_ wrong. I
           | do not mean this as a moral judgment, I mean it as an
           | engineering process diagnostic feedback. I literally can 't
           | count the number of times it has popped up a bug that I
           | wouldn't have expected because of some change.
           | 
           | It is that very characteristic that makes me love them so
           | much. No matter how carefully you code you can never get away
           | from the problem of a small change over there causing a
           | breaking over here because of something you couldn't even
           | have anticipated, but you don't have to wait until some
           | large-scale QA process or even production deployment to find
           | out; you can find out 15 seconds later, and then fix it, or
           | realize your new change is fundamentally untenable, or any
           | number of things. I'd say "I don't know how people develop
           | without these things", except I do; the code bases are
           | treated like quick-set concrete and nothing can be changed
           | once laid down. What a stultifying way to code. I would hate
           | to work at a job like that.
        
             | dwaite wrote:
             | > Then you, or somebody near you, is doing it very wrong. I
             | do not mean this as a moral judgment, I mean it as an
             | engineering process diagnostic feedback.
             | 
             | IMHO thats the biggest problem with TDD. People often think
             | of TDD as a set of requirements that all push for some set
             | of benefits.
             | 
             | But it is really a development methodology where you as a
             | developer heavily utilize tests to drive the development
             | process itself. It is a mindset, not a checklist.
             | 
             | Perhaps easiest to explain (with an inaccurate comparison)
             | as - it is like REPL-driven development but with persistent
             | and sharable artifacts.
             | 
             | Behavioral driven development is IMHO stronger in this
             | regards, because people tend to understand how the
             | additional artifacts (beyond what you might see with a
             | requirements list or use case document) are part of a
             | methodology. People can better separate the methodology
             | from the benefits/outcome.
        
             | major505 wrote:
             | Here is the thing: Most managers want you to implement the
             | test and the code in the same time It would take to write
             | only the code.
             | 
             | The thing is, in the end most devs do shit tests becase
             | theres no time allocated to that, and the test end up being
             | just a number that needs to be met so the code could run to
             | the dev ops engine and generte a new version of the
             | software.
        
           | nonethewiser wrote:
           | I think it can be taken too far but why not have some unit
           | tests? They are the easiest kind of test to write and the
           | fastest to run.
           | 
           | Also, you dont see it because those bugs were fixed before
           | production.
        
           | spiddy wrote:
           | That's because by definition it is not going to be "major"
           | problem since the unit test acted as gateway before it got
           | pushed to production, instead you'll probably be 'meh' and
           | fix it once the unit test fails.
           | 
           | This reminds me the saying of a manager arguing why do we
           | need so many SREs since the system is working fine.
        
           | dwaite wrote:
           | Test driven development on one project would often save me
           | weeks per bug fix.
           | 
           | This was because it maintained an environment where I could
           | replicate portions of the business logic (as assembled
           | modules) outside a production environment. This made it much
           | easier to do analysis/fuzzing of one component of the system,
           | vs trying to replicate and do post-mortem crash analysis on
           | the entire deployed system.
           | 
           | But I clarify this is TDD, not unit tests. A development
           | methodology where unit tests are wedged in after the fact do
           | not promote the sort of modular programming needed to be able
           | to do this.
        
         | donatj wrote:
         | > people just write useless tests that test absolutaly nothing
         | usefull, and are worth nothing.
         | 
         | Oh man, those are my favorite. They're super difficult to
         | critique in code review. "This test doesn't really test
         | anything" isn't very helpful but writing a proper "do this" is
         | often more work than writing a proper test yourself.
        
           | nerdponx wrote:
           | Is that not helpful? It's definitely helpful.
           | 
           | "This test doesn't do anything", then reject & request
           | changes, "add substantive tests that cover the following
           | invariants: ...".
        
           | major505 wrote:
           | Thats the thing. Its hard to describe, but you know when you
           | see it.
        
         | jerf wrote:
         | Unit tests need to: A. Be taken seriously by the dev team. B.
         | Taken at least _moderately_ seriously by management. Getting
         | the unit tests passing isn 't always the most important thing
         | _this very second_ , but they do need to be something we get
         | back to. C. Run automatically, and preferably as a gateway to
         | the commit process. Some sort of automatic continuous
         | integration thing is critical. I have a lot of smaller projects
         | rather than one big project so my CI is often just a git pre-
         | commit hook. This works fine at a small scale; if "CI" is less
         | than ten seconds I think it's advantageous to keep it as local
         | to the dev as possible rather than shoving it off to some
         | external server to complain later.
         | 
         | Without these things, they're mostly theatre. With them,
         | they're an incredibly valuable aspect of development that I
         | don't intend to ever develop with ever again, by which I mean,
         | if I do end up taking a job where this wasn't already in place,
         | and I'm not allowed to put it into place, I will shortly be at
         | a new job. Life is too short for the kinds of _stupid_
         | debugging you have to put in to an untested, uncovered code
         | base. (I don 't mind debugging in general. It's a fact of the
         | job. But that _pull your hair out every single time_ , that's
         | an uncovered code base.)
        
         | [deleted]
        
         | HideousKojima wrote:
         | Also most examples of unit tests that I see in various examples
         | I've seen amounted to testing "Does the code do what the code
         | does?" Unless you have a clear spec to implement, or you have
         | some weird edge cases that you need to make sure get handled
         | correctly, I see very little use in unit tests (integration
         | tests are very helpful though). The only time I've really used
         | unit tests were when we had some specific financial formulas
         | that we were implementing and needed to make sure the code
         | matched them.
        
         | major505 wrote:
         | PS: dont take me wrong. Unit tests are useful if used properly.
         | But people just don't use it correctly.
         | 
         | To most people what is important is the percentage of code
         | tested, instead of test critical logic conditions for your
         | software piece.
         | 
         | I work in a big bank in my country, that would insist us to do
         | unit test in UI. Things like, if I set the button visibility to
         | true, the button needs to be visible. Like... no shit Sherlock?
         | What do you expect to happen?
        
       | dctoedt wrote:
       | Navy nuclear-propulsion people will recognize "TLD" as the
       | acronym for thermo-luminescent [radiation] dosimeter, which we
       | all had to wear whenever in "the plant" back in the day, and
       | which would be read by a machine once a month to check on our
       | radiation exposures. (Don't know whether they still use TLDs.)
        
       ___________________________________________________________________
       (page generated 2023-01-21 23:01 UTC)