Minish is a small property-based testing framework for Zig, inspired by QuickCheck and Hypothesis.
Property-based testing is a way of testing software by defining properties that should always hold. Compared to typical example-based testing (like unit tests), instead of writing individual test cases with specific inputs and expected outputs, you define general properties about your code's behavior. The testing framework then generates a wide range of random inputs to verify that these properties hold for all cases.
Given a piece of code like a function and its property, a property-based testing workflow normally involves the following steps:
- Generating a lot of random inputs.
- Finding cases where the input causes the property to fail.
- Finding smaller subsets of the failing input that still cause the failure (this is called "shrinking").
For example, consider the property of a reverse function that states that reversing
a string twice should return the original string.
In property-based testing, you would define this property and let the framework generate a lot of random strings to
test it.
If it finds a string that makes the property fail (due to a bug in the reverse function, for example), it will then try
to shrink that string to a simpler or shorter case that still makes the property fail.
Here is a brief comparison between example-based testing and property-based testing paradigms:
| Criterion | Example-based Testing | Property-based Testing |
|---|---|---|
| Input | Hand-written specific values | Auto-generated random values |
| Coverage | Only cases you can think of | Discovers edge cases automatically |
| Debugging | Exact failing inputs are known | Shrinks to minimal failing case |
| Effort | Write a lot of test cases | Define one property, test with many inputs |
- Written in pure Zig with no external dependencies
- Includes over 25 built-in generators (like for integers, floats, strings, lists, structs, UUIDs, timestamps, etc.)
- Combinators to build complex generators from simple ones (map, filter, flatMap, frequency)
- Supports automatic shrinking for integers, floats, strings, lists, tuples, and arrays
- Supports reproducible failures and verbose mode
- Configurable and easy to integrate into existing Zig projects
See ROADMAP.md for the list of implemented and planned features.
Important
Minish is in early development, so bugs and breaking changes are expected. Please use the issues page to report bugs or request features.
You can add Minish to your project and start using it by following the steps below.
Run the following command in the root directory of your project to download Minish:
zig fetch --save=minish "https://github.com/CogitatorTech/minish/archive/<branch_or_tag>.tar.gz"Replace <branch_or_tag> with the desired branch or release tag, like main (for the development version) or v0.1.0.
This command will download Minish and add it to Zig's global cache and update your project's build.zig.zon file.
Note
Minsih is developed and tested with Zig version 0.15.2.
Next, modify your build.zig file to make Minish available to your build target as a module.
pub fn build(b: *std.Build) void {
// ... the existing setup ...
const minish_dep = b.dependency("minish", .{});
const minish_module = minish_dep.module("minish");
exe.root_module.addImport("minish", minish_module);
}Finally, you can @import("minish") and start using it in your Zig project.
const std = @import("std");
const minish = @import("minish");
const gen = minish.gen;
// Helper function to reverse a string
fn reverse(allocator: std.mem.Allocator, s: []const u8) ![]u8 {
const result = try allocator.alloc(u8, s.len);
for (s, 0..) |c, i| {
result[s.len - 1 - i] = c;
}
return result;
}
// Property: reversing a string twice returns the original
fn reverse_twice_is_identity(s: []const u8) !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
const once = try reverse(allocator, s);
defer allocator.free(once);
const twice = try reverse(allocator, once);
defer allocator.free(twice);
try std.testing.expectEqualStrings(s, twice);
}
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
// Generate random strings and test the property
const string_gen = gen.string(.{
.min_len = 0,
.max_len = 100,
.charset = .alphanumeric,
});
try minish.check(allocator, string_gen, reverse_twice_is_identity, .{
.num_runs = 100,
});
}You can find the API documentation for the latest release of Minish here.
Alternatively, you can use the make docs command to generate the documentation for the current version of Minish.
This will generate HTML documentation in the docs/api directory, which you can serve locally with make serve-docs
and view in a web browser.
Check out the examples directory for example usages of Minish.
See CONTRIBUTING.md for details on how to make a contribution.
Minish is licensed under the Apache License, Version 2.0 (see LICENSE).
- The logo is from SVG Repo with some modifications.