How to write unit tests for methods that use Knex.
See original GitHub issue(Originally posted in #1659, moved here for further discussion.)
Iām kind of in the dark on this one - the knex docs cover the barebones of transactions, but when I google āusing knex.js for unit testingā and āava.js + knexā and āava.js + databaseā, I couldnāt find anything particularly instructive to show me a good way, so I fell back on my Ruby experience where wrapping a unit test in a transaction is a common technique for reseting the DB after each test.
@odigity I wouldnāt suggest of doing that because your tests will end up using just one connection instead of connection pool and it will not represent real world usage of your app.
That did occur to me, but I was willing to accept that if it allowed me to use a simple, write-once method for implementing post-test DB cleanup. (I did set my pool min/max to 1 to be sure.) If thereās a way to accomplish this while supporting concurrent connections, I will absolute embrace that.
Also its pretty much impossible to implement unless the application you are testing is running its queries in the same transaction that was started inside test code (you would need to start transaction in test then start your app and pass the created transaction to app so that it makes all the queries to the same transaction and nested transactions might behave starngelyā¦).
The way I hoped/expected it to work is:
-
In each unit test, I create a transaction which produces
trx
. -
I then require the module I want to test and pass the
trx
object to the module constructor so it will be used by the module, thus resulting in all queries happening inside the transaction. -
After the module method returns (or throws error), I run my assertions on the resulting state of the DB, then call
trx.rollback()
to undo everything from the start to prepare for the next test.
So, thatās what Iām trying to achieve and how I originally intended to achieve it. Iām eager to learn more about:
-
In what whys Iām misunderstanding how Knex.js works and should be used.
-
Best practices for writing atomic unit tests for code that touches the database.
Issue Analytics
- State:
- Created 6 years ago
- Reactions:8
- Comments:36 (13 by maintainers)
Nope - got nothing. Summary in chronological order of sources:
2016-04-21 https://medium.com/@jomaora/knex-bookshelf-mocks-and-unit-tests-cca627565d3
Strategy: Use mock-knex. I donāt want to mock the DB - I want to test my methods against an actual MySQL DB to ensure correct behavior - but I took a look at
mock-knex
anyway⦠it may just be the worst designed library Iāve ever encountered. š¦2016-04-28 http://mherman.org/blog/2016/04/28/test-driven-development-with-node/
Strategy: Rollback/remigrate/reseed DB after each test. That seems like a great deal of overhead for each test, and will run terribly slow. Concurrency could be achieved by generating a UUID for each testās DB name, but that just seems like an awful solution compared to the elegance of transactionsā¦
2015-09-23 http://stackoverflow.com/a/32749601/210867
Strategy: Use sqlite for testing, create/destroy DB for each test. Iāve covered both reasons I dislike this approach above.
So⦠still fishing for suggestions, and additional guidance on how Knex transactions actually work, as well as how to properly apply them to my use case.
@odigity nicely summed up the bad testing practices š
We are doing our āunitā tests like this:
Start up system, initialize DB and run migrations
Before each test we truncate all the tables and sequences (with knex-db-manager package)
Insert data required for the test case (we use knex based objection.js ORM for that which allows us to insert nested object hierarchies with single command, it knows how to optimise inserts so that it doesnāt have to do separate insert for each row in the table, but usually just one insert per table)
run 1 test and goto step 2
With e2e tests we have implemented saveState / restoreState (with pg_restore/pg_dump) methods, which allows us to roll back to certain state during the test run, so we donāt have to restart test run every time when some test fails after running 20 minutes of tests.