[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)