[HN Gopher] HumbleObject (2020)
___________________________________________________________________
HumbleObject (2020)
Author : mooreds
Score : 29 points
Date : 2023-06-23 21:39 UTC (1 days ago)
(HTM) web link (martinfowler.com)
(TXT) w3m dump (martinfowler.com)
| pphysch wrote:
| The fundamental problem here is "imperative GUI" incentivizing
| tight coupling between business logic and presentation.
|
| Whereas "declarative GUI" like the browser DOM can be understood
| & interacted by automated tools _without_ instrumentation of the
| business logic.
|
| Adding _more_ tight coupling, by coupling your code architecture
| to the limitations of your testing framework, seems like going in
| the wrong direction in most cases.
|
| Some programs like games need imperative GUI controls to minimize
| latency, but trying to automatically test everything in an AAA
| game is a losing battle. You need QA eyeballs, period (or
| sophisticated visual AI agents).
| PaulHoule wrote:
| I dunno. Sometimes it seems that 90% of the complexity of
| making web apps is figuring out details, corner cases, and
| spooky action at a distance with CSS. For crying out loud, _the
| CSS spec comprises 50 documents_
|
| https://www.w3.org/TR/css-2023/
|
| It's one thing to test that element A has the right classes on
| it, it's another thing to test that a certain CSS selector that
| is sensitive to the context works correctly, that precedence
| rules work as expected, or ultimately the document "looks
| right".
| pphysch wrote:
| CSS is (too) powerful but not inherently complex. It is often
| abused.
|
| Ultimately you will need real eyeballs to judge aesthetics.
| No argument there. But significant QA work can be automated
| with frontend testing frameworks.
| rerdavies wrote:
| I've never been that impressed with the functional coverage
| you get from front-end-driven testing frameworks, at least
| those that drive views. Test that interface with models in
| the front-end, though -- priceless!
|
| And CSS is inherently complex, because the rules of
| precedence are fragile and easily broken.
| rerdavies wrote:
| Or unsophisticated agents. A strategy I've used for testing
| highly visual code: run the test procedure; take a snapshot of
| graphic output; generate a hash signature of the output (e.g.
| CRC32); if the signature has not changed, the test passes; if
| the signature has changed, queue the test to a human for
| verification; if the human accepts the output, save the new
| hash signature for the next automated test.
|
| The TL/DR: if the output hasn't changed the test passes; if it
| has, get a human to approve the change. Developers can (and
| probably should) perform most of the manual verification.
|
| It wouldn't completely eliminate test teams for AAA games; but
| it could substantially reduce the load placed on test teams,
| allowing them to focus on what test teams do best.
| pizza wrote:
| Quick and dirty simple solution - nice!
| ajanuary wrote:
| Where I hit this all the time is when people have written various
| flavours of queues and item processors all wrapped up into one,
| where a timer or similar mechanism will trigger an item(s) to be
| processed. For example, a denouncing queue where you can add
| items to it, and it will pause a bit to allow other items to
| accumulate in the queue that can be processed in a single batch.
|
| To test the item processing well, you need lots of different
| tests covering all the branches in the logic.
|
| But to trigger the processing you need carefully timed code full
| of sleeps.
|
| Combine the two and you have lots of painfully slow tests.
|
| Move the logic out so you can test that nice and fast, then have
| a handful of slow tests checking the queue mechanism works.
|
| Contrary to some of the other comments here, I don't think that's
| designing your code specifically for your test framework. The
| logic of how you coordinate processing is a separate thing to the
| logic of how you process it. So splitting those concerns apart is
| good design regardless.
| jbandela1 wrote:
| Does anybody else get nervous over architectural patterns that
| exist only/mainly to make unit testing easier?
|
| My preference for these type of situations is to try to
| encapsulate as much logic as I can into pure functions. In
| addition to be being easy to test, they also have additional
| architectural benefits that go beyond testing.
| TOGoS wrote:
| From my own experience: Occasionally it feels awkward, but more
| often than not, the same patterns that make testing easier also
| lead to an architecture that is easier for me to understand and
| reason about, because 'having a minimal set of well-defined
| functions' happens to be good for both, and also leads to
| better composability, etc etc.
|
| And even in cases where they are not the same, I find that the
| confidence that automated tests give me is worth the little bit
| of gymnastics[1] I have to do to make the things testable.
|
| YMMV. :-P
|
| [1] And maybe I should note that I stay clear of all the fancy
| gizmos that 'modern' testing frameworks provide. If you need to
| mess around with a mocking framework, that means you did it
| wrong. It's hard to improve on `assertEquals( expectedOutput,
| doTheThing(input) )`.
| PaulHoule wrote:
| Sometimes it is an improvement, sometimes it is
|
| https://dhh.dk/2014/test-induced-design-damage.html
| TOGoS wrote:
| > This presentation shows the fundamentals of the
| "decoupling from Rails"
|
| I 100% understand why someone would want to do that,
| though, for unit testing or otherwise. Rails (and every
| framework inspired by it) seems almost intentionally
| designed to force you into coding a big ball of mud where
| everything is held together with magic framework duct tape.
| I guess some people's brains are wired to enjoy working in
| big mud balls, but for those of us who don't, adding a
| decouple-from-Rails-or-Laravel-or-Spring-Boot-or-what-have-
| you layer is the only hope we have of continuing to receive
| a paycheck while keeping our sanity intact.
| scubbo wrote:
| > If you need to mess around with a mocking framework, that
| means you did it wrong
|
| This is a debate that's been going around my professional
| circle for a while, I'd love to hear a bit more about your
| take on it. My position is fairly opposed to this - that
| mocks are a valuable tool allowing you to they test
| situations that are not reachable based purely on input (if
| you're not mocking, "how does my code behave in the presence
| of error responses from a dependency?" can only be tested by
| hard-coding the dependency with "when you encounter this
| particular input, short-circuit your logic and return an
| error"), and let you test otherwise-slow logic quickly.
|
| I can see, and agree with, the argument that tests which rely
| on mocking have their confidence bounded-above by your
| confidence in the correctness of your mocking (that is - if
| you test against incorrect expectations, your tests will pass
| for code that will fail in production. GIGO), but to my mind
| that doesn't mean that tests based on mocks are inherently
| bad - simply that their restrictions must be acknowledged.
| They have strengths and weaknesses, and are most valuable as
| part of a suite of testing strategies. Mocks themselves are
| not inherently bad, even though an over-reliance on them is.
| PaulHoule wrote:
| Testing is at its best when you are testing functions or small
| subsystems that can be created and destroyed independently and
| be initialized with a known state.
|
| For instance the kind of things you write in an introductory
| programming class (somehow it goes over the heads of most
| students that the teachers are using automated testing.) It's
| certifiably insane to not test parsing code, whether or not it
| is "functional" (input text return a data structure) or not
| (function gets called to consume text, callbacks get called)
|
| UI code is tricky because usually the framework is calling into
| your code and your code is getting called in a shambolic way.
| Of course it problematizes testing but it problematic anyway. I
| have been looking at testing of a React application again and
| boy is it crazy, where you have to deal with asynchronous
| updates and don't really know how many times the app will
| update itself before you know it has "settled down" and it is
| OK to test the "output". Testing is bad, with tests that should
| take 0.04 sec taking more like 4 sec, but the user is being
| subjected to the same thing with lots of waiting, layout
| shifts, flashes of incorrect information, etc. And don't get me
| started with the typical "uncontrolled" component which is
| completely out of control since some application state updating
| updates the state managed by React, other ones call _freaking
| jQuery_ (talk about spooky action at a distance!)
| blueprint wrote:
| that's virtually precisely what the article is suggesting
| mrkeen wrote:
| > architectural patterns that exist only/mainly to make unit
| testing easier?
|
| > try to encapsulate as much logic as I can into pure functions
|
| These are one and the same. The suggestion may as well have
| been "the IO monad is hard to test, so move as much code as you
| can out into pure functions"
| regularfry wrote:
| Not really. It might start out being for the benefit of the
| tests but knock-on benefits turn up, usually sooner rather than
| later.
| scubbo wrote:
| When I was a young nerd, I was a rabid fan of logic,
| objectivity, science. Anything which smacked of spirituality
| (or, worse, religion) was immediately dismissed as useless
| sentimental nonsense. I recognized that some people might
| _enjoy_ thinking about these fripperies, but I was convinced
| that any phenomenon which claimed an effect but did not explain
| the science-based interactions that mediated it (think
| astrology or religion) was hogwash.
|
| One day I was deathly bored on a school trip, and the only
| entertainment available was a book on Feng Shui. During the
| introduction, it advised the practice of imagining a dragon
| entering a living space through a door, coiling their way
| through and around the space, and then exiting. The following
| sentence stated that this was often a good heuristic for
| helping the human mind judge a space for sight lines, common
| travel paths, and avoiding clutter.
|
| My mind was blown. The dragon wasn't the point - the dragon was
| a way to hack the human brain (itself a messy, complex, poorly-
| understood mechanism) to a better outcome than it would get by
| a straightforward series of explicable evaluations. If the
| process demonstrably leads to better outcomes - if it is
| "useful" - then it can still provide value even if you can't
| explain _why_ it works[0]
|
| All of which is a roundabout way of getting to my point, which
| is: no, I don't get nervous about architectural patterns which
| _appear_ to only exist to make unit testing easier, because
| overwhelmingly I have found that "is this architecture easy to
| test?" is a good heuristic for "does this architecture have
| well-divided areas and layers of responsibility?". No matter
| your opinion on the value of tests _themselves_ (and there are
| very cogent arguments that an over-reliance on unit testing
| alone can lead to brittleness and false confidence), ceteris
| paribus making your architecture "more testable" will usually
| be a good thing in itself, even before any tests are written.
|
| [0] this attitude needs some nuance, of course, when it is
| actually important to explain how and why the process reaches
| conclusions. See: ML/LLMs/AGI/AI/whatever-the-current-
| initialism-is, and recourse in the face of disputed judgements
| or unforeseen effects.
| KnobbleMcKnees wrote:
| Depends on the definition of easier. If easier is "it won't
| work with my particular flavour of framework or mocking" then
| yes, it's a bad idea.
|
| If it's inherently untestable for any setup, that's already a
| smell and needs some sort of attention. If the smell is
| accompanied by potential to cause catastrophic error or such,
| I'd rather lean in to changing the code to be more testable
| even if it feels like an anti-pattern.
| jacknews wrote:
| So structure your code for the testing methodology/mechanism?
|
| Sorry, but that's BS. TDD works to some extent, when it's used as
| 'specification, then code', but solving the problem is the main
| thing, not testing that it solves the problem.
| ramses0 wrote:
| I've had the privilege of refactoring age-old code, and it takes
| more than two hands to count the number of times I've named a
| class "TestableMegaFoo", whereby little testable bits of
| "MegaFoo" have been judiciously migrated to.
|
| There's another comment in here talking about feng-shui and
| dragons, I'll recount the story of "peace pipes" and "killing
| your enemies".
|
| It's tough to find in the modern internet, but I remember reading
| about Native American traditions of gathering around a piece pipe
| and applauding your enemies, looking for the good in them,
| effectively.
|
| The story goes that if you do this enough, your enemy
| "disappears" and you see them as friends.
|
| So it can be with code. Class MegaFoo simply becomes calls to
| "TestableMegaFoo", with a few usual side-effect inducing "less
| testable components". Your enemy disappears in a puff of smoke.
| lincpa wrote:
| [dead]
___________________________________________________________________
(page generated 2023-06-24 23:02 UTC)