duct
duct copied to clipboard
Duct tests with integrant
The Duct unit test example only cover init a handler with no dependences which is impossible in real world. Currently, if I want to do integration test with db. I need to do something like
(defn config-fixture [keys deps]
(fn [f]
(let [config (-> (duct/read-config (io/resource "test.edn"))
duct/prep
(dissoc :duct.core/handler))] ; remove handler because it will start the jetty
(let [sys (ig/init config keys)]
(ig/run! sys keys (fn [_ db]
(reset! deps db)
(f)
(reset! deps nil)
(ig/halt! sys keys)))))))
(def system (atom nil)) ; this is the trick to use ig dependents
(use-fixtures :once (fixtures/config-fixture
[:duct.database/sql]
system))
(deftest db-itest
(testing "create dumb in db"
(let [dumbs (-> (s/gen :dumb/dumb)
(gen/sample 100))
db-spec @system
apply-dissoc #(apply dissoc %1 %2)]
(is db-spec "db-spec should exists")
(doseq [dumb dumbs]
(let [expected dumb
actual (db/create db-spec dumb)]
(is (= expected actual) "inserted dumb should be same as input"))))))
is there any better method to test with integrant
?
The primary use case is for testing all the boundaries in duct, to see if they are healthy.
Ideally in Duct you'll have three types of tests:
- System tests that
init
the entire system and test it from end to end - Integration tests that test the boundaries against the database
- Unit tests that use mocks or stubs of the boundary protocols
So my initial advice is to test the boundary functions directly:
(deftest db-test
(jdbc/with-db-transaction [tx db-spec]
(jdbc/db-set-rollback-only! tx)
(seed-database tx)
(let [db (->Boundary tx)]
...)))
If you want to test boundary functions in conjunction with your handler, perhaps because you're uncertain of the interactions between the handler and the database, then you could use init-key
directly:
(deftest db-test
(jdbc/with-db-transaction [tx db-spec]
(jdbc/db-set-rollback-only! tx)
(seed-database tx)
(let [db (->Boundary tx)
handler (init-key :foo.handler/bar {:db db})]
...)))
Obviously you can wrap a lot of the above in a macro or function to automate the process of setting up the transaction and seeding the database.
I would consider my tests falling into second case, that's why we will have code that's only pick the keys needed. As the example you provided, seed-database
would be some calls to ragtime, since the ragtime configuration is a bit duct-ized in config.edn
, if we don't touch the integrant for migrator.ragtime
we needs to add some code that's similar to migrator.ragtime to seed the database.
That makes sense. In which case, rather than using init-key
, you can use a selective init
.
(deftest db-test
(jdbc/with-db-transaction [tx db-spec]
(jdbc/db-set-rollback-only! tx)
(let [config (assoc test-config :duct.database/sql {:connection tx})
system (-> config duct/prep (ig/init [:foo.handler/bar]))
handler (:foo.handler/bar system)]
...)))