Liking cljdoc? Tell your friends :D

Testing

See Testing Strategy for the conceptual overview. This guide covers the day-to-day commands and patterns.

Test commands

# Every test surface (monorepo: also the standalone libs and tools)
bb test:all

# Every tests.edn suite
clojure -M:test:test/all

# Single library
clojure -M:test :core
clojure -M:test :user

# By tag
clojure -M:test:test/all --focus-meta :unit
clojure -M:test:test/all --focus-meta :integration
clojure -M:test:test/all --focus-meta :contract

# Single namespace
clojure -M:test:test/all --focus user-property-test

# Watch mode
clojure -M:test --watch :core
clojure -M:test:test/all --watch --focus-meta :unit

# Auth tests (JWT secret required)
JWT_SECRET="dev-secret-at-least-32-characters-long" clojure -M:test :user

Writing unit tests

Tag with ^:unit. Call core functions directly - no setup needed:

(ns wagoe.product.core.product-test
  (:require [clojure.test :refer [deftest is testing]]
            [wagoe.product.core.product :as product-core]))

(deftest ^:unit test-prepare-product
  (testing "returns a product with a generated id"
    (let [now    (java.time.Instant/now)
          result (product-core/prepare-product {:name "Widget" :price 9.99M} now)]
      (is (uuid? (:id result)))
      (is (= "Widget" (:name result)))
      (is (= 9.99M (:price result)))))

  (testing "adds timestamps"
    (let [now    (java.time.Instant/now)
          result (product-core/prepare-product {:name "Widget" :price 9.99M} now)]
      (is (inst? (:created-at result)))
      (is (inst? (:updated-at result))))))

Writing integration tests

Tag with ^:integration. Use in-memory adapters to test service orchestration:

(ns wagoe.product.shell.service-test
  (:require [clojure.test :refer [deftest is use-fixtures]]
            [wagoe.product.shell.service :as service]
            [wagoe.product.shell.adapters.in-memory :as mem]))

(defn with-service [f]
  (let [repo (mem/create-in-memory-repo)]
    (binding [*service* (service/create-product-service {:repo repo})]
      (f))))

(use-fixtures :each with-service)

(deftest ^:integration test-create-product
  (let [result (service/create-product *service* {:name "Widget" :price 9.99M})]
    (is (= "Widget" (:name result)))
    (is (uuid? (:id result)))))

Writing contract tests

Tag with ^:contract. Test against a real H2 in-memory database; wagoe.test (the test library) boots the app’s system on the test profile and drops the database afterwards:

(ns wagoe.product.shell.persistence-test
  (:require [clojure.test :refer [deftest is]]
            [my-app.system-config :as system-config]
            [wagoe.product.shell.persistence :as persistence]
            [wagoe.test :refer [with-test-system]]))

(deftest ^:contract test-round-trip
  (with-test-system [sys system-config/ig-config]
    (let [db      (:wagoe/db-context sys)
          product {:id (random-uuid) :name "Widget" :price 9.99M
                   :created-at (java.time.Instant/now)}
          _       (persistence/create-product! db product)
          found   (persistence/find-product-by-id db (:id product))]
      (is (= (:name product) (:name found)))
      (is (= (:price product) (:price found))))))

Test suite in tests.edn

Add new libraries to tests.edn:

#kaocha/v1
{:kaocha/source-paths [;; existing paths ...
                       "libs/product/src"]
 :tests [;; existing suites ...
         {:id          :product
          :test-paths  ["libs/product/test"]
          :ns-patterns ["wagoe.product.*-test"]}]}

AI test generation

bb ai gen-tests src/<your-project>/product/core/product.clj

Generates a complete test namespace from the source file using Wagoe conventions. The model sees the real protocols and vars of every namespace the source requires. It prints to stdout; --write writes it to the conventional test path, -o to a file you name. The answer is linted as bb check lints it, and retried once with the errors; if it still fails nothing is written unless you pass --force. It is run, in a separate JVM, only with --run. Each deftest is tagged ^:unit unless it does real I/O.

Can you improve this documentation? These fine people already did:
Thijs Creemers & thijscreemers
Edit on GitHub

cljdoc builds & hosts documentation for Clojure/Script libraries

Keyboard shortcuts
Ctrl+kJump to recent docs
←Move to previous article
→Move to next article
Ctrl+/Jump to the search field
× close