Contract.Test.Assert
module provides a DSL for assertions that accumulate error messages, instead of exiting early after the first failure.
There are two kinds of tests:
-
ContractAssertion
type: assertions that can raise recoverable errors withtellFailure
-
ContractCheck
type: Checks can inspect the state both before and afterContract
execution, allowing to monitor for effects, e.g. monetary gains/losses at address
runChecks
function accepts an array of checks and a Contract
action to run, lifted to ContractAssertion
to allow for assertion accumulation (lifting can be done with Control.Monad.Trans.Class.lift
):
runChecks
:: forall (a :: Type)
. Array (ContractCheck a)
-> ContractAssertion a
-> Contract a
To convert an assertion into a ContractCheck
, Contract.Test.Assert.assertionToCheck
can be used:
assertionToCheck
:: forall (a :: Type)
. String
-> (a -> ContractAssertion Unit)
-> ContractCheck a
The first argument is a descriptive message that will be printed in case there is an exception thrown inside the Contract
that is being tested. This is done because in case of an exception there's no way to get the result value of type a
. For better developer experience, all the tests skipped due to the exception will still be mentioned in the report, e.g. for this example:
✗ Examples.ContractTestUtils:
Error: An exception has been thrown:
Error: (some error message)
at Object.exports.error (/home/me/c/cardano-transaction-lib/output/Effect.Exception/foreign.js:8:10)
at Object.$$throw [as throw] (/home/me/c/cardano-transaction-lib/output/Effect.Exception/index.js:18:45)
at /home/me/c/cardano-transaction-lib/output/Ctl.Examples.ContractTestUtils/index.js:131:506
at /home/me/c/cardano-transaction-lib/output/Control.Monad.Reader.Trans/index.js:108:34
at run (/home/me/c/cardano-transaction-lib/output/Effect.Aff/foreign.js:278:22)
at /home/me/c/cardano-transaction-lib/output/Effect.Aff/foreign.js:348:19
at drain (/home/me/c/cardano-transaction-lib/output/Effect.Aff/foreign.js:120:9)
at Object.enqueue (/home/me/c/cardano-transaction-lib/output/Effect.Aff/foreign.js:141:11)
at /home/me/c/cardano-transaction-lib/output/Effect.Aff/foreign.js:339:27
at /home/me/c/cardano-transaction-lib/output/Effect.Aff.Compat/index.js:18:55
The following `Contract` checks have been skipped due to an exception:
1. Sender's output has a datum
2. Output has a reference script
3. Contains CIP-25 metadata
ContractCheck
is defined as follows:
type ContractCheck a =
ContractAssertion a
-> ContractAssertion (ContractAssertion a /\ ContractAssertion Unit)
- The argument is the
Contract
to be tested itself - The outer
ContractAssertion
can perform effects to initialize internal assertion state (e.g. creation ofRef
s that can be used by actions in the tuple) - The first component of the tuple is the
Contract
that is supposed to be executed. It can be modified in the outerContractAssertion
action, but most checks would simply return it unchanged. - The second component of the tuple is a finalization action, that is executed after the first component is run, regardless of whether the result of execution was success or an exception. This allows to implement checks that inspect the state change before and after, as well as the mechanism of skipped assertions reporting.
Particular values can be constructed with utility functions, as demonstrated in the ContractTestUtils example (see mkAssertions
).
An example for using checks in the tests is here.
All the functions require Labeled
arguments, that can be constructed with label
function; or noLabel
, if descriptive names in error messages are not needed.