Skip to content
Back to skills

Clojure Test Check

ASecurity

Property-based testing with clojure.test.check. Use when writing generative tests, creating custom generators, or debugging shrinking behavior. Triggers on test.check imports, prop/for-all, gen/* usage, or questions about property-based testing in Clojure.

  • 666 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added February 8, 2026
testinggotestingdebugging

Security analysis

A100/100

Scanned February 12, 2026

npx -y skills add majiayu000/claude-skill-registry --skill clojure-test-check --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Clojure Test Check?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Clojure Test Check
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/majiayu000-clojure-test-check/badge)](https://www.skillsdirectory.com/skills/majiayu000-clojure-test-check)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: clojure-test-check
description: Property-based testing with clojure.test.check. Use when writing generative tests, creating custom generators, or debugging shrinking behavior. Triggers on test.check imports, prop/for-all, gen/* usage, or questions about property-based testing in Clojure.
---

# test.check

Property-based testing generates random test data to verify properties hold across many inputs. Instead of specific test cases, define universal properties.

## Core Pattern

```clojure
(require '[clojure.test.check :as tc]
         '[clojure.test.check.generators :as gen]
         '[clojure.test.check.properties :as prop])

(def my-property
  (prop/for-all [v (gen/vector gen/small-integer)]
    (= (count v) (count (reverse v)))))

(tc/quick-check 100 my-property)
;; => {:result true, :pass? true, :num-tests 100, ...}
```

## clojure.test Integration

```clojure
(require '[clojure.test.check.clojure-test :refer [defspec]])

(defspec sort-is-idempotent ;; 100 optional  iterations value here
  (prop/for-all [v (gen/vector gen/small-integer)]
    (= (sort v) (sort (sort v)))))
```

Important! Prefer NOT to supply an iterations, prefer the framework defaults

## Essential Generators

| Generator | Produces |
|-----------|----------|
| `gen/small-integer` | Integers bounded by size |
| `gen/large-integer` | Full range integers |
| `gen/nat` | Non-negative integers |
| `gen/boolean` | true/false |
| `gen/string` | Strings |
| `gen/keyword` | Keywords |
| `(gen/vector g)` | Vectors of g |
| `(gen/tuple g1 g2)` | Fixed heterogeneous vectors |
| `(gen/elements coll)` | Random element from coll |
| `(gen/one-of [g1 g2])` | Choose generator randomly |

See [references/cheatsheet.md](references/cheatsheet.md) for complete generator reference.

## Generator Combinators

**fmap** - Transform generated values:
```clojure
(gen/fmap sort (gen/vector gen/small-integer))  ; sorted vectors
(gen/fmap set (gen/vector gen/nat))             ; sets from vectors
```

**such-that** - Filter values (use sparingly, only for likely predicates):
```clojure
(gen/such-that not-empty (gen/vector gen/boolean))
```

**bind** - Chain generators (value from first determines second):
```clojure
(gen/bind (gen/not-empty (gen/vector gen/small-integer))
          #(gen/tuple (gen/return %) (gen/elements %)))
```

**let** - Macro combining fmap/bind:
```clojure
(gen/let [a gen/nat
          b gen/nat]
  {:sum (+ a b) :a a :b b})
```

## Custom Record Generators

```clojure
(defrecord User [name id email])

(def user-gen
  (gen/fmap (partial apply ->User)
            (gen/tuple gen/string-alphanumeric
                       gen/nat
                       (gen/fmap #(str % "@example.com") gen/string-alphanumeric))))
```

## Recursive Generators

```clojure
(def json-like
  (gen/recursive-gen
    (fn [inner] (gen/one-of [(gen/list inner)
                             (gen/map gen/keyword inner)]))
    (gen/one-of [gen/small-integer gen/boolean gen/string])))
```

## Shrinking Failures

When a test fails, test.check automatically shrinks to minimal failing case:

```clojure
;; Fails on vectors containing 42
(tc/quick-check 100
  (prop/for-all [v (gen/vector gen/small-integer)]
    (not-any? #{42} v)))
;; :fail [[-35 -9 42 8 31 ...]]  <- original failure
;; :shrunk {:smallest [[42]]}    <- minimal case
```

## Critical Pitfalls

**Avoid gen/bind when gen/fmap suffices** - bind hampers shrinking:
```clojure
;; Bad: shrinks poorly
(gen/let [n (gen/fmap #(* 2 %) gen/nat)]
  (gen/vector gen/large-integer n))

;; Good: shrinks well
(gen/fmap #(cond-> % (odd? (count %)) pop)
          (gen/vector gen/large-integer))
```

**Use gen/large-integer for real integer testing** - small-integer/nat cap at ~200.

**Run at least 100 tests** - Size cycles through 0-199, fewer tests means poor coverage.

**Don't use such-that for unlikely predicates** - Will timeout. Use fmap to construct valid values instead.

See [references/advanced.md](references/advanced.md) for sizing/shrinking details.

## Common Property Patterns

**Roundtrip**: `(= x (decode (encode x)))`
**Idempotence**: `(= (f x) (f (f x)))`
**Invariant preservation**: `(= (count v) (count (sort v)))`
**Commutativity**: `(= (f a b) (f b a))`
**Model comparison**: `(= (my-impl x) (reference-impl x))`

See [references/examples.md](references/examples.md) for generator recipes.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.