I have a question related to testing. We accept th...
# http4k
c
I have a question related to testing. We accept that for our tests we need the db in some cases (e2e-tests). We create an "app" (HttpHandler) with the same function as we use in production, and use that app to run the test code against. During the request cycle it makes db requests (we use one db transaction per HTTP request), but in test the transaction is rolled back instead of committed (
jdbi.transactionHandler = RollbackOnlyTransactionHandler()
) at the end of the request cycle. During tests I'd love to open the db transaction the the start of the test, do some initialization, then pass that transaction to the "app" along the request, and after the response is back still have the transaction for in-transaction-db-inspection. This means that instead of passing the function that creates my "app" a db connection pool, I pass it an
EitherDbPoolOrOpenTransaction
. I want to give this a try, but thought I'd ask here; since I might not be the first cowboy who thinks db calls during test are acceptable 🙂 (I write a significant part of the application in Postgres -- triggers, caching tables, invariants, immutability)
a
I write all my tests against a real database too, but I've never tried to do what you want to do. I actually never even share a transaction outside a single repository method, and either design to allow for incomplete updates, or perform the cleanup at the app level. So here's a few suggestions: 1. It seems to me that a low-level test like this could be scoped down to your repository class itself. There's likely little benefit in involving the entire app for what seems like a corner-case. So in that case, you would just open the transaction in your test harness, perform the init, and then pass the transaction into your repository method. 2. If you inject a DataSource into your app factory instead of a connection string (which seems to be the case), you could pass in a wrapper that executes your custom setup logic once the nth transaction is opened. Not sure how feasible this is. 3. If you follow the same pattern as I do, where transactions never persist outside a single repository method, then you may find better opportunities to inject your failure state.
c
Thanks Andrew for your response. The tests you describe in 1, I also have them: at least one per SQL query. During test runs, they also run in rollback-only transactions to keep tests isolated. I dont really have repositories (they looked like an OO/Java'ism to me; I just put all my SQL queries in top-level functions that are extensions on a "Db" interface). What you suggest in 2 & 3 sound more difficult than creating a sum-type that contains either the datasource, or the jdbi-handle-with-pre-started-transaction-in-rollback-only-mode, and passing that to the app.
So I implemented it with the sum type. The tests all still pass (even run a little faster! lol) It did give me this monstrosity:
Copy code
var globalTxHandleForE2eTests: Handle? = null

sealed class DbSource {
  class WrappedJdbi(val jdbi: Jdbi) : DbSource()
  class WrappedTxHandleForE2eTests() : DbSource() {
    fun txHandle(): Handle = globalTxHandleForE2eTests!!
  }
}
Now I can use the same transaction in the @Test annotated function as is used in the HttpHandler app. It's dirty but it works and is only used in test scenarios.