This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2021-06-13
Channels
- # babashka (1)
- # beginners (11)
- # cider (8)
- # clj-kondo (7)
- # clojure (35)
- # clojure-italy (2)
- # clojurescript (6)
- # conjure (5)
- # datomic (10)
- # duct (7)
- # fulcro (9)
- # helix (2)
- # introduce-yourself (5)
- # lsp (3)
- # malli (7)
- # meander (3)
- # off-topic (8)
- # pathom (3)
- # podcasts-discuss (2)
- # portal (9)
- # reitit (7)
- # releases (3)
- # shadow-cljs (43)
- # sql (25)
- # tools-deps (3)
How to mock a database, for example, with next.jdbc? Do we extend some protocol with test specific methods or ?
@i That's a pretty broad question. My recommendation -- generally -- is to use a real DB locally (or in a VM) that you can setup and teardown as needed.
If you have separated your side effecting code from your pure logic, you can mock the side effecting functions (not the DB itself).
Say I have a (save-user-requirements xxx) function, which internally uses (save-to-db! db) function. I need to mock the save-to-db! function into something without communicating with the real db.
I think when I am testing (save-to-db!) function itself, I need use some real db. But when I am testing the (save-user-requirement) function, I need find a way to properly mock out the (save-to-db!) function.
So basically I found (with-redefs) and (defprotocol) to mechanism to alter (save-to-db!), I am not sure which one is more common.
Your problem is that your save-user-requirements
domain logic function calls a side-effecting function.
Separate those and you don't have to worry about "testing the db".
What does save-user-requirements
do that needs testing (aside from the "trivial" persistence function that shouldn't need testing, right)?
If you're going to mix logic and persistence, use a real DB and live with the consequences. Otherwise, properly separate your logic from your persistence and just test your logic.
;; trasforms the data
(defn- save-user-requirements-impl [data])
(defn save-user-requirements [data db]
(-> data
save-user-requirements-impl
(write-to-db db)))
If I separate the persistence logic from the (pure) business one. Then all the save-user-requirement-alike function skeleton will look like the above
Kinda like some contrived repetitive code structure, designed in a specific way for the testing purpose only.
Right, which should suggest that you need to think more broadly about how you make this separation at all levels.
Yes. I’ve been thinking about a general clojure application layout containing external state interactions (such as db, web) for some time but couldn’t settle down on something natural to me. Do you have any good codebase for reference?
What we usually do is to have a real test db like Sean suggests. A lot of times you really need it for end to end tests. We have a macro that will create a transaction from the test-db-spec and wrap the test's body, inside which, you can use this transaction wherever you usually pass a db-spec or transaction or connection. The macro will wrap the body in a try, finally block and in the finally block will rollback the transaction. Example:
(with-test-txn [txn (test-db-spec)]
(testing "some test"
(write-answer-to-db txn 42)
(is (= 42 (get-answer-from-db txn))
@UMPJRJU9E Thanks. The example you give is about testing the (write-to-db) function. How do you test (save-user-requirements) as mentioned in this thread?
The save-user-requirements that you broke down looks good. This is usually how I write domain logic for a vertical. The ns usually has one entry function acting as the interface to the outer world, that in turn calls various pure and impure functions. The pure functions can be easily unit tested and the interfacing function is end to end tested with data setup in the test db. Sometimes the impure functions can be mocked like when they're calling external APIs or if it is a DB use a test DB. Since (save-user-requirements)
is internally calling the DB, you could with-redef the function that gets you your db-spec to return the test-db-spec instead.