Canister

Unit testing for canisters

The Tester class in this module can be used to define unit tests for canisters.

type Protocol = {#start : Nat; #cont : [Text]; #done : [Text]}

type TestResult = {#success; #fail : Text}

class Tester(options : { batchSize : Nat })

public func should(name : Text, test : () -> async TestResult)

Registers a test. You can use attempt to use a Matcher to produce a TestResult.

public func shouldFailTo(name : Text, test : () -> async ())

Registers a test that should throw an exception.

public func runAll() : async Text

Runs all your tests in one go and returns a summary Text. If calling this runs out of gas, try using run with a configured batchSize.

public func run() : async Protocol

You must call this as the last thing in your unit test.

Instantiate one of these on canister initialization. Then you can use it to register your tests and it will take care of running them.

Use runAll for simple setups and run once that stops working.

When using run the Tester will execute your tests in the right batch sizes. It will keep calling your test function over and over, so make sure to not do any work outside the registered tests.

import Canister "canister:YourCanisterNameHere";
import C "mo:matchers/Canister";
import M "mo:matchers/Matchers";
import T "mo:matchers/Testable";

actor {
    let it = C.Tester({ batchSize = 8 });
    public shared func test() : async Text {

        it.should("greet me", func () : async C.TestResult = async {
          let greeting = await Canister.greet("Christoph");
          M.attempt(greeting, M.equals(T.text("Hello, Christoph!")))
        });

        it.shouldFailTo("greet him-whose-name-shall-not-be-spoken", func () : async () = async {
          let greeting = await Canister.greet("Voldemort");
          ignore greeting
        });

        await it.runAll()
        // await it.run()
    }
}