Mine is that the useState hook is an actual plague on the entire React ecosystem. Regardless of your opinions of React, if you wanna use something then use it right.

  • AeonFelis@lemmy.world
    link
    fedilink
    arrow-up
    3
    ·
    5 days ago

    And they pin behavior more than strict correctness, in my experience, but that’s a valuable signal. Canaries in the mine.

    The problem is that the behavior they pin is not the official API-exposed behavior. The behavior they pin is mostly implementation details. If diffuse(bomb) is supposed to diffuse the bomb, I need to test that given a legal instance of a Bomb it’d end up diffused. A unit test cannot test that - if diffuse is the unit being tested, then bomb is not allowed to be an actual bomb - it has to be a mock. And thus diffuse(bomb) does not test that diffuse diffuses a bomb - it tests that diffuse cuts very specific set of wires in a very specific order.

    • If the bomb change, the unit test is till green because the mock is still replaying the old behavior.
    • If I fix diffuse to handle the new bomb, the unit test becomes red.

    Once the bomb change, the unit test shows the opposite of what a test is supposed to show - and now you have to fix the test itself. Because that’s what unit tests test - they test themselves. They don’t test the system.

    An integration test that would bring in an actual bomb, diffuse it, and check if it’s diffused in a controlled environment where it can’t do damage by blowing up - is going to be more expensive to run, yes, but at least you will be getting a meaningful signal out of it. It’d be testing something of value. The unit test is cheap to run (although writing it was probably more expensive), but it gives you nothing. It’s completely pointless.

    • Windex007@lemmy.world
      link
      fedilink
      arrow-up
      2
      ·
      edit-2
      4 days ago

      Awesome and well articulated response. Love it.

      There are a few threads to pull here:

      Is an integration test the best way to test the full e2e behavior of an api call? Yep.

      Now, depending on the size (and architecture) of your system, the full test suite of into tests could take like, 10 hours.

      And even then, you’re STILL probably mocking something (direct seeding bombs into the db). Full setup of all life cycles of all your entities using their associated entities (bombs, wires, wire clippers, bomb diffusers, their rank, their bosses, their diffusion history, etc…)

      So they’re expensive, and if you really look you’re probably still cheating some steps anyways.

      Another thread to pull on is what are you mocking in your tests to begin with, and if they’re not resulting in valuable test results then that goes back to one of my original points being that you might have suboptimal patterns in your production code.

      Like, what logic governs bomb diffusion and where does it live? Are you mocking the right thing? If you have to mock the wrong thing… WHY? The ritual of writing those tests give you the first whiff of a potential smell.

      I tell my guys not to test implementation: test behaviour. Test contract adherence. If that’s hard, no problem, message me let’s review it. Maybe a behavior they’re trying to test legitimately should be an int test. More often than not, their design is demanding to mock something that should be decomposed further, and then composed with dependency injections that are where the units that should ACTUALLY be mocked are the things that get mocked and injected, so the unit tests are actually testing the BEHAVIORS we want to pin rather than implementations. Composition and single responsibility principal are what enable unit tests to pull their weight.

      Edit: I’m so passionate on the subject that if you scribbled up a scratch example and pinged me a github link I’d genuinely review it.

      • AeonFelis@lemmy.world
        link
        fedilink
        arrow-up
        1
        ·
        11 hours ago

        Now, depending on the size (and architecture) of your system, the full test suite of into tests could take like, 10 hours.

        You’ll eventually have to run these 10 hours no matter what. If you only run the unit tests you won’t be able to tell if your code actually works, and if your full e2e suite passes then you won’t learn anything of value from running the unit tests - if one of them fails you can be fairly certain that it is that unit test itself that is broken, not the product.

        During development, you don’t need to run the full test suite. It’s usually quite trivial to identify a single integration test that checks the parts you are touching, and then you only have to run it to see the effects of your changes.

        And even then, you’re STILL probably mocking something (direct seeding bombs into the db). Full setup of all life cycles of all your entities using their associated entities (bombs, wires, wire clippers, bomb diffusers, their rank, their bosses, their diffusion history, etc…)

        Integration tests rarely (if ever) need mocks. If they need Test Doubles at all, they’d typically be _Fake_s rather than _Mock_s. But even that is not always needed - an integration test can often just use non-production versions of the real dependencies.

        So - if your real software typically runs against a database that sits on a powerful server and has tables with billions of rows, the e2e tests can use the same database engine but run it locally and only put a few dozens of rows in these tables. This setup is quick and cheap to set up and tear down while still being faithful to the production setup - maybe not 100%, but orders or magnitude more faithful than a unit test’s mock that just compares the SQL commands given to it with the ones preprogrammed into it.

        Another thread to pull on is what are you mocking in your tests to begin with, and if they’re not resulting in valuable test results then that goes back to one of my original points being that you might have suboptimal patterns in your production code.

        Like, what logic governs bomb diffusion and where does it live? Are you mocking the right thing? If you have to mock the wrong thing… WHY? The ritual of writing those tests give you the first whiff of a potential smell.

        My argument is that this is more about the “problem domain” than about the “solution domain”. Some field of software work very neatly with unit tests - general libraries are a good example. Other fields are a very poor fit, and trying to shoehorn them into using unit tests - even worse, shoehorning them into using mostly unit tests - will inevitable result in a nightmare of mock maintenance.

        I tell my guys not to test implementation: test behaviour. Test contract adherence. If that’s hard, no problem, message me let’s review it. Maybe a behavior they’re trying to test legitimately should be an int test. More often than not, their design is demanding to mock something that should be decomposed further, and then composed with dependency injections that are where the units that should ACTUALLY be mocked are the things that get mocked and injected, so the unit tests are actually testing the BEHAVIORS we want to pin rather than implementations. Composition and single responsibility principal are what enable unit tests to pull their weight.

        The problem is that unit tests “promote” aspects of implementation into behavior. Classic example - the exact syntax of the SQL query a function uses to obtain the data it needs is by all rights an implementation detail, but a unit test is forbidden from using a real database so it passes a mock that expects a very specific query - now it became a behavior.