Wheatley

standard-readme compliant pub package likes pub points building

Let you code generate your test cases for you!

Wheatley is a no frills property based testing (PBT) library for Dart, spiritual successor to Glados.

Background

Explaining the basics and virtues of property based testing is beyond the scope of this README, but if you are new to property based testing, or just want to learn more, here are some resources to get you started:

Here's a great video about how to think about getting started with PBT: https://youtu.be/wHJZ0icwSkc?si=xGv88gLZBo0Evx7F

Install

Just add wheatley to your dev dependencies in pubspec.yaml:

dev_dependencies:
  wheatley: <current version>

Usage

import wheatley.dart and use forAll to verify that your properties hold true for all generated inputs, e.g.:

import 'package:test/test.dart';
import 'package:wheatley/wheatley.dart';

void main() {
  test('My property is always true', () {
    forAll(myGenerator)((value) => expect(value.myProperty, isTrue));
  });
}

You can configure forAll with an Explore config to control how many iterations to run, how fast to to scale the size of generated value, and even which Random instance to use.

Using more than one generator

Generating a new value from two or more other generated values can be done in two different ways.

The first option is to use the provided convenience versions of forAll (forAll2, forAll3, etc.), e.g.:

void main() {
  test('My property is always true', () {
    forAll3(g1, g2, g3)((v1, v2, v3) => expect(MyClass(v1, v2, v3).myProperty, isTrue));
  });
}

You can also use zip, to create a new generator from existing ones on-the fly:

void main() {
  test('My property is always true', () {
    forAll((g1, g2, g3).zip)((value) => expect(MyClass(value.$1, value.$2, value.$3).myProperty, isTrue));
  });
}

Writing custom generators.

A generator for a type T (Generator<T>) is just a function that receives a Random instance and a size, and returns a Candidate. A Candidate is essentially just a value, but a value that also knows how to make itself less "complex", if possible. What complex means for a particular value is really up to the author of the generator.

GeneratorExtensions defines a number of utilities to make it more convenient to create new custom generators based on the already existing default ones. Arguably the most important one being zip, to combine two existing generators into one, e.g.:

Generator<MyClass> myClass = (g1, g2, g3).zip.map((values) => MyClass(values.$1, values.$2, values.$3));

Note: forAllX and zip are currently only have implementations for up to 8 values. Please open an issue if have need of more than that.

Using generators for non-property based testing

Generators can easily be used to generate test input data for traditional example based tests that. Just pass ExploreConfig.single() to forAll and Wheatley will only test your code with a single example and not try to shrink the input if the test fails.

Thanks

Huge thanks to Marcel Garus for creating the original Glados library, which Wheatley has drawn a lot of inspiration from.

Contributing

Contributions welcome! Please open a PR or an issue if you have any suggestions or improvements.

Libraries

wheatley