• nous
    link
    fedilink
    English
    arrow-up
    1
    ·
    7 days ago

    I generally think mocking should be the last resort tool you reach for when testing - not just databases but anything. Test with real things where you can and you build in much fewer assumptions about their behavior. Though I also dislike testing against things that the tests have no control over. Which the author does not go into any detail on at all and is the big pit fall that makes testing against real things a huge pain.

    If possible an in process or at least an temporary in memory substitute should be used over the real thing. For instance if you are using sqlite or (an ORM that supports it) this is trivial. Each test can have its own instance that does not interfere with other tests (which is where flaky tests tend to come from) and everything being local is fast enough for unit tests.

    Lacking that fake implementations are a good bet as well. A good example of that is this goflakes3 package that acts as a in memory implementation of the s3 API in go. Again it is fast, you can spin up isolated instances in your test and it bakes in fewer assumptions about how s3 works in your tests (and if its assumptions are false they can be fixed for everyone where with mocks you have to find and fix every mock you have written).

    Even things like filesystem interactions are easy to test for real on - I never understood peoples obsession with mocking out filesystem calls. If your filesystem is unreliable you have bigger problems to worry about and go fix. Isolation is the only real concern here but that is trivial to achieve by creating a temp file or directory unique to each test case. And the only change needed for your code is to be able to change the prefix for where it looks for the files/directories in question (which is often something you want to support anyway).

    We really need to stop mocking out everything and picking mocks up as the first tool when you want to test something. Isolating a test to only test a single function in isolation of everything else just means you need to write more tests and that the tests don’t end up testing as much as you fail to test the assumptions about interactions between your components. As long as your tests are fast and reliable there is little need to break them down into tiny component bits and mocking things they need to use.

    Use real implementations if you can. Fakes if you cannot control the real implementation and mocks as a last resort.