MyNixOS website logo
Description

A lightweight enumeration-based property testing library.

Tinycheck is a deterministic property testing library. Instead of random generation, test cases are produced by exhaustive, fairly-interleaved enumeration. It integrates with the Tasty test framework via Test.Tasty.TinyCheck.

tinycheck

A lightweight, deterministic property testing library for Haskell.

Instead of generating random inputs, tinycheck enumerates test cases from a canonical, fairly-interleaved ordering. Tests are reproducible, require no seeds, and cover small values first — no shrinking required.

Quick start

import Test.Tasty
import Test.Tasty.TinyCheck

main :: IO ()
main = defaultMain $ testGroup "my suite"
  [ testProperty "reverse . reverse == id" $
      \(xs :: [Int]) -> reverse (reverse xs) == xs
  , testProperty "abs x >= 0" $
      \(x :: Int) -> abs x >= 0
  ]

Run with cabal test.

How it works

The core type is TestCases a — a newtype over [a] whose Semigroup, Applicative, and Monad instances use fair interleaving instead of concatenation and cartesian product.

TestCases [1,2,3] <> TestCases [10,20,30]  ==  TestCases [1,10,2,20,3,30]

With infinite generators this ensures neither side is starved:

(Left <$> TestCases [1..]) <> (Right <$> TestCases [1..])
  ==  TestCases [Left 1, Right 1, Left 2, Right 2, ...]

Applicative interleaves function-argument pairs, so all parts of the input space are explored immediately rather than exhausting one argument before moving to the next.

Use interleaveN to interleave any number of generators fairly:

interleaveN [TestCases [1,2,3], TestCases [10,20,30], TestCases [100,200,300]]
  ==  TestCases [1,10,100, 2,20,200, 3,30,300]

Defining generators

Implement Arbitrary for your types, or derive it via Generically:

{-# LANGUAGE DeriveGeneric #-}
import GHC.Generics (Generic, Generically (..))
import Data.TestCases (Arbitrary)

data Colour = Red | Green | Blue
  deriving stock (Show, Generic)
  deriving (Arbitrary) via Generically Colour

Newtype wrappers are provided for common patterns:

WrapperSuitable for
SignedArbitraryNum + Enum (e.g. Int, Integer)
BoundedArbitraryBounded + Enum (e.g. Bool, Word8)
RealFracArbitraryFractional + Enum (e.g. Float, Double)
......

Preconditions

Use ==> to skip inputs that don't satisfy a precondition:

testProperty "n > 0 implies n * 2 > 0" $
  \(n :: Int) -> (n > 0) ==> property (n * 2 > 0)

Development

Modules

ModulePurpose
Data.TestCasesCore TestCases type, Arbitrary, CoArbitrary
Test.Tasty.TinyCheckTasty integration (testProperty, testPropertyWith, …)

examples/

Two standalone examples for defining Arbitrary instances for your own types are provided here.

AI usage

The central pieces are developed by a human, @turion. Many details (tasty integration, boilerplate, test cases, longer docs) have been generated by AI (Github Copilot with Claude Opus & Sonnet 4.6) and are thoroughly checked by myself.

Metadata

Version

0.2.0

Platforms (80)

    Darwin
    FreeBSD
    Genode
    GHCJS
    Linux
    MMIXware
    NetBSD
    none
    OpenBSD
    Redox
    Solaris
    uefi
    WASI
    Windows
Show all
  • aarch64-darwin
  • aarch64-freebsd
  • aarch64-genode
  • aarch64-linux
  • aarch64-netbsd
  • aarch64-none
  • aarch64-uefi
  • aarch64-windows
  • aarch64_be-none
  • arc-linux
  • arm-none
  • armv5tel-linux
  • armv6l-linux
  • armv6l-netbsd
  • armv6l-none
  • armv7a-linux
  • armv7a-netbsd
  • armv7l-linux
  • armv7l-netbsd
  • avr-none
  • i686-cygwin
  • i686-freebsd
  • i686-genode
  • i686-linux
  • i686-netbsd
  • i686-none
  • i686-openbsd
  • i686-windows
  • javascript-ghcjs
  • loongarch64-linux
  • m68k-linux
  • m68k-netbsd
  • m68k-none
  • microblaze-linux
  • microblaze-none
  • microblazeel-linux
  • microblazeel-none
  • mips-linux
  • mips-none
  • mips64-linux
  • mips64-none
  • mips64el-linux
  • mipsel-linux
  • mipsel-netbsd
  • mmix-mmixware
  • msp430-none
  • or1k-none
  • powerpc-linux
  • powerpc-netbsd
  • powerpc-none
  • powerpc64-linux
  • powerpc64le-linux
  • powerpcle-none
  • riscv32-linux
  • riscv32-netbsd
  • riscv32-none
  • riscv64-linux
  • riscv64-netbsd
  • riscv64-none
  • rx-none
  • s390-linux
  • s390-none
  • s390x-linux
  • s390x-none
  • sh4-linux
  • vc4-none
  • wasm32-wasi
  • wasm64-wasi
  • x86_64-cygwin
  • x86_64-darwin
  • x86_64-freebsd
  • x86_64-genode
  • x86_64-linux
  • x86_64-netbsd
  • x86_64-none
  • x86_64-openbsd
  • x86_64-redox
  • x86_64-solaris
  • x86_64-uefi
  • x86_64-windows