| > For some time proponents of unit testing have asserted that unit tests should not touch a real database Is that still a belief in some circles? I feel like the shift away from this started like 15 years ago (largely because of RoR in my mind). Anyways, this essentially launches a pg instance with a postgresql.conf that is aimed for speed (at the risk of possible data loss/corruption). DO NOT DO THIS IN PRODUCTION, but I just bake the following in local/test/CI: fsync = off
synchronous_commit = off
Some other things I've seen / done in the name of testing with PG:- Use unlogged tables. Maybe it's a bit faster, never really seemed to make much a different. - Drop all foreign keys. This has significant non-performance impact. On the downside, it materially changes the environment that your tests are running vs your system: test could now pass for otherwise invalid code. On the plus side, it makes setting up fake data _a lot_ easier. - Run tests with a pool_size of 1. This catches cases where you start a transaction, but forget to use the transaction object, e.g.: db.begin_transaction(tx ->
tx.query("update x")
db.query("delete y") // should have been tx.query
)
- A robust Factory library (NIH or OSS) is pretty probably the most important thing you can do- If you can't pre-seed all the test data, try to write _all_ your tests without ever having to truncate/delete tables between tests. This (a) avoids the slow delete/truncate and (b) lets you run tests in parallel. This means using random keys to avoid duplicates (obviously uuid is the simple answer here) and not selecting all data from a table when verifying/asserting (since who knows what's going to be in there). |
We use an embedded postgres in our DB tests, and we call those 'Integration Tests' and run them separately than the pure unit tests. While still tremendously valuable, they do take a bit longer to run, and currently aren't written to allow parallel tests running.
We've had a typical habit of writing most tests that hit the DB. Since applying a bit more discipline to remove the DB requirement, we've found it's a more pleasant experience with the quicker feedback loop in place.
Your last point is a good one - tests that can run in isolation and are agnostic to the presence of other data (and ideally clean up after themselves) tend to be handy.