Live data from Hacker News

Database mocks are not worth it

shayon.dev

171–180 of 268 posts

Re: Database mocks are not worth it

#171

Earlier quoted context omitted.

how do you test the imperative shell except with mocks? Suppose you're calling an external API

The imperative shell is untestable with unit tests. You don’t test it this way period. Think about it. This IO layer should be so thin that all you’re doing is fetching and getting so this layer does a direct external call. When you mock this entire thing is basically what is mocked. So if you mock or don’t mock if you write your code with the pattern of functional core and imperative shell the imperative shell is wh…

> whats the point?

suppose you are writing some software that interfaces with personal finance SAAS. except you want your users to be able to interact with more than one. so you sure as shit better be testing that your abstractions are making the correct calls, especially if customer money is involved.

you also really want to be testing for unreliability, for example, if your saas call is taking too long, you dont double tap, if a tx looks like it fails but it didnt actually, you know that on the second attempt, etc, etc.

"the shell is untestable" is not an acceptable answer.

and you cant wriggle out of this by saying "well that's an integration test". you said in your op "don't write mocks". thar is terrible advice in general. im generally a big fan of imperative shell functional core, but you MUST mock your shell, especially if absolute correctness is required (people's lives or money at stake)

Re: Database mocks are not worth it

#172

Don’t code with mocks period. Structure your code such that it has a functional core and imperative shell. All logic is unit testable (functional core). All IO and mutation Is not unit testable (imperative shell). Mocks come from a place where logic is heavily intertwined with IO. It means your code is so coupled that you can’t test logic without touching IO. IO should be a dumb layer as much as possible. It should b…

Any suggestions on where to read about this more?

https://www.destroyallsoftware.com/talks/boundaries

Re: Database mocks are not worth it

#173

Earlier quoted context omitted.

The database is often the thing that enforces the most critical application invariants, and is the primary source of errors when those invariants are violated. For example, "tenant IDs are unique" or "updates to the foobars are strictly serializable". The only thing enforcing these invariants in production is the interplay between your database schema and the queries you execute against it. So unless you exercise the…

There's something very, very wrong in the way we write programs nowadays. Because yeah, the database is you main source of invariants. But there is no good reason for you application environment not to query the invariants from there and test or prove your code around them. We do DRY very badly, and the most vocal proponents are the worst... But I don't think this is a good example of the principle failing.

> no good reason for you application environment not to query the invariants from there and test or prove your code around them

As a developer who primarily builds backend web applications in high level languages like golang and java I run the risk of sounding ignorant talking like this but.. I'm led to believe lower level systems and embedded software has a lot more invariant preserving runtime asserts and such in it. The idea being that if an invariant is violated better to fail hard and fast than to attempt to proceed as if everything is alright.

Re: Database mocks are not worth it

#174

Earlier quoted context omitted.

how do you test the imperative shell except with mocks? Suppose you're calling an external API

How do you test that the external API returns are correct?

mock it and then just assert of the mocked result - never fails :)

Re: Database mocks are not worth it

#175
post #5

I thought this was common knowledge and that it became even easier after Docker became a thing? Mocks are wishful thinking incarnate most of the time, though here and there they are absolutely needed (like 3rd party APIs without sandbox environments, or quite expensive API, or most of the time: both). Just pick a task runner -- I use just[0] -- and make a task that brings up both Docker and your containers, then run…

Too many people use fakes and call them mocks. Which makes the conversations quite tortured.

Re: Database mocks are not worth it

#176

Don’t code with mocks period. Structure your code such that it has a functional core and imperative shell. All logic is unit testable (functional core). All IO and mutation Is not unit testable (imperative shell). Mocks come from a place where logic is heavily intertwined with IO. It means your code is so coupled that you can’t test logic without touching IO. IO should be a dumb layer as much as possible. It should b…

I am actually at a point in my career that I won’t work for a company that is dependent on stored procedures. Rollbacks, versioning, branching, testing and everything else is a pain once you introduce stored procedures.

As a lead for mostly green field applications over the past decade, my one no compromise architectural edict is no stored procedures for online production code.

I might use it for background utilities or one off maintenance.

Re: Database mocks are not worth it

#177

Don’t code with mocks period. Structure your code such that it has a functional core and imperative shell. All logic is unit testable (functional core). All IO and mutation Is not unit testable (imperative shell). Mocks come from a place where logic is heavily intertwined with IO. It means your code is so coupled that you can’t test logic without touching IO. IO should be a dumb layer as much as possible. It should b…

> IO should be a dumb layer as much as possible. It should be Extremely general and as simple as fetching and requesting in the stupidest way possible. Then everything else should be covered by unit tests while IO is fundamentally not unit testable. Another way to put this is to ignore all the cool problems your database can solve for you and hand roll it instead. This also is a recipe for riddling your codebase with…

Right, but you can see how they do it. The problems they are trading are likely not hard for them.

Whatever you use, in your personal toolbox changes the results of the tradeoffs.

Re: Database mocks are not worth it

#178

As per usual Elixir does this correctly and even has a setup that allows all tests to run against the database in a pristine way in parallel: https://hexdocs.pm/ecto_sql/Ecto.Adapters.SQL.Sandbox.html

A hobby project of mine (in Elixir) uses SQLite as primary database. Each test runs in its own fully isolated SQLite database. No mocking (or transaction rolling back) needed. Most of these tests take less than 1ms to run (and when they take longer, it's because of something else). This kind of setup makes the usual Ecto Sandbox approach feel slow, but I do agree that the way Elixir approaches this is great!

Do you have a link you can share that demonstrates the details of this approach?

Re: Database mocks are not worth it

#179
post #5

I thought this was common knowledge and that it became even easier after Docker became a thing? Mocks are wishful thinking incarnate most of the time, though here and there they are absolutely needed (like 3rd party APIs without sandbox environments, or quite expensive API, or most of the time: both). Just pick a task runner -- I use just[0] -- and make a task that brings up both Docker and your containers, then run…

I've had a nice experience using Docker Compose for this, although you might still want a task runner wrapper around it. Podman Compose should work fine as well.

My current setup on a personal project is docker compose with Postgres and pgadmin container.

I would never think of mocking a database in 2024/2025, just spin one up.

Also keep your lynch pin invariants in the database and not in your code.

Re: Database mocks are not worth it

#180
post #71

Earlier quoted context omitted.

The point of mocking the database is to avoid the hassle of spinning up a database to test against. If we ignore that, why not test our use of the database? Are we disinterested in the schema and query errors we might catch? Unit testing is useful because (a) it's easier to test all the code paths in a unit when that unit is isolated, and (b) unit tests don't need to be modified in response to changes in unrelated mo…

> why not test our use of the database Primarily slowness and brittleness. Tests with side effects are much more likely to be flaky, and flaky tests are the worst. Especially if they're slow and hence hard to debug. Of course, you do test your use of the database, but in a much smaller test suite. For the business logic tests you just spin up a copy of your app with fake adapters, and you're done. Quick, deterministi…

I agree that integrated tests tend to be brittle, but if we need database tests either way, the database harness can't be flaky. So any flakiness there is something that has to be fixed regardless. Slowness, I agree, but unless you're spinning up new containers for every test, the overhead of each query being real is going to be small.

If the database interactions are trivial, I agree—just use stubs. But if you've got non-trivial joins, then you'll need a separate database test suite anyway. And if there's stateful logic involving the database and you want to use fakes in unit tests, you need a whole extra set of tests to validate that the fakes behave the same way that the real database does.

I do actually prefer to avoid relying on the database for unit tests—it's cleaner, it's faster—but often testing with the database is simpler & reliable, and I can't justify the extra lines of code required for fully isolated unit tests. That's extra surface area for tech debt that could lead to stuff like fakes behaving differently from their production equivalent.

Post reply on HN