Test hardening library for Common Lisp. Hardens your tests so they don't lie to you.
Your tests pass. Coverage is 100%. Then production breaks.
Why? Your tests lied. They passed without actually verifying behavior.
Test hardening fixes this with six techniques:
- Property-Based Testing — Test algorithms with 1000 random cases, not 3 examples
- Mutation Testing — Introduce bugs to verify tests actually catch them
- Contract Testing — Fast integration tests without brittle mocks
- Agent Verification — Multi-dimensional checks for AI-generated code
- Test Harness — Eliminate test boilerplate with declarative environment setup
- Test Fixtures — Reusable test data with factory functions
Learn why test hardening matters →
(ql:quickload "cl-test-hardening/all")Property Testing:
(use-package :th.property)
(use-package :th.gen)
(defproperty reverse-twice-identity (list)
(equal list (reverse (reverse list))))
(check-property 'reverse-twice-identity
:generator (gen:lists (gen:integers))
:num-tests 100)
;; ✓ Passed 100 testsMutation Testing:
(use-package :th.mutation)
(define-mutation-policy arithmetic-check
:operators '(add-to-sub sub-to-add)
:threshold 0.8)
(run-mutation-analysis 'arithmetic-check
'("src/calculator.lisp")
"sbcl --script run-tests.lisp")
;; Score: 0.85 (85%) Killed: 85 Survived: 15Contract Testing:
(use-package :th.contract)
(define-contract user-api
:consumer "web-app"
:provider "user-service"
:interactions
((interaction "get user"
:request ((:method "GET") (:path "/users/123"))
:response ((:status 200) (:body ((:id 123) (:name "Alice")))))))
(with-mock-provider ('user-api :port 8080)
(let ((user (fetch-user 123)))
(assert (equal "Alice" (user-name user)))))
;; ✓ Consumer test passed- Quickstart (5 minutes) — Installation and first tests
- Why Test Hardening? — Philosophy and benefits
Learn by building real examples:
- Property-Based Testing Tutorial
- Mutation Testing Tutorial
- Contract Testing Tutorial
- Test Organization Tutorial — Harness & Fixtures
Complete function documentation:
- API Overview
- Property Testing API
- Test Harness API
- Test Fixture API
- Mutation Testing API (coming soon)
- Contract Testing API (coming soon)
- Agent Verification API (coming soon)
- Glossary — Key terms explained
Load individual modules as needed:
(ql:quickload "cl-test-hardening/property") ; Property-based testing
(ql:quickload "cl-test-hardening/mutation") ; Mutation testing
(ql:quickload "cl-test-hardening/contract") ; Contract testing
(ql:quickload "cl-test-hardening/agent") ; Agent verification
(ql:quickload "cl-test-hardening/harness") ; Test environment setup
(ql:quickload "cl-test-hardening/fixture") ; Test fixtures
(ql:quickload "cl-test-hardening/all") ; EverythingEach module is independent — load only what you need.
- Common Lisp (SBCL recommended)
- Quicklisp
- FiveAM (for property testing integration)
Run all tests:
(ql:quickload "cl-test-hardening/tests")
(5am:run! :th.tests)
;; All 1064 tests passStable — All modules functional, tests passing, ready for use.
See canonical-specification/ for formal specifications.
For development guidance, see CLAUDE.md.
MIT License. See LICENSE for details.
Abhijit Rao (@quasi) — quasiLabs Consulting