Skip to content

Test library

elci test provides three modules, written in Elci, to your test modules: Expect for expectations, Scenario for transactions against your validators, and Property for property-based tests. This page lists every type and function they expose. For a guided introduction, start with Unit tests.

Only test modules can import these modules. An import from a source module is an error at the import, ELCI2101: elci test reports Only test modules can import Expect; it is part of the test library, not available to elci build. A test module cannot be named Expect, Scenario or Property.

A test is an exposed, top-level declaration of a test module that is either:

  • a zero-argument value of type Bool, Expect.Expectation, Scenario.Scenario or Property.Property; or
  • a function whose result type is Property.Property, which runs as a property with generated arguments.

Every other declaration is a helper. Modules run in alphabetical order and the tests of a module in source order.

elci test optionEffect
--match <substring>runs only the tests whose Module.name contains the substring, ignoring case
--seed <n>runs properties with seed n; without it, a random seed is used and printed
--fuzz <runs>runs each property runs times (default 100; must be positive)

The command exits with status 1 when a test fails or when no test is found or matches. See also the CLI reference.

import Expect exposing (Expectation)
type Expectation
= Pass
| Fail String (List Data)

A failure carries a message and the Data encodings of the values involved, which the test runner prints. The constructors are exposed, so a helper can match on an expectation with import Expect exposing (Expectation(..)).

FunctionSignaturePasses when
passExpectationalways
failString -> Expectationnever
equala -> a -> Expectationexpected == actual, for equal expected actual
notEquala -> a -> Expectationunexpected /= actual, for notEqual unexpected actual
atMostcomparable -> comparable -> Expectationactual <= bound, for atMost bound actual
atLeastcomparable -> comparable -> Expectationactual >= bound, for atLeast bound actual
lessThancomparable -> comparable -> Expectationactual < bound, for lessThan bound actual
greaterThancomparable -> comparable -> Expectationactual > bound, for greaterThan bound actual
everyList Expectation -> Expectationevery expectation in the list passes; otherwise the first failure is the result
onFailString -> Expectation -> Expectationthe expectation passes; otherwise its message is replaced, keeping its values

equal and notEqual require the same structural equality as ==. Types without it, such as Map, functions and imported types without approved equality, are rejected at compile time with ELCI2003. Data has it: Expect.equal (Data.encode expected) datum compares structure. atMost, atLeast, lessThan and greaterThan take any comparable type, as in elm-test: integers, strings, bytes, ledger hashes, and lists and tuples of these.

Values are printed as their Plutus Data encoding: integers as numbers, bytes and strings as #hex, lists as [...], and records, tuples, Bool, Maybe and custom types as Constr tag [fields].

ExpectationReport
equalExpect.equal, then expected: and actual:
notEqualExpect.notEqual, then values: with the actual value
atMost, atLeast, lessThan, greaterThanthe function name, then values: with the bound and the actual value
fail messagethe message
onFail message ethe message, then the values of e, if any
import Scenario exposing (Scenario)

A scenario is data describing one transaction against a validator of the project. The runner compiles the named validator as elci build does, applies the scenario’s parameters, builds the complete ledger context with julc-testkit and runs the deployed script. The validator’s own address, minting policy and staking credential are derived from the applied script’s hash. See Transaction scenarios.

type Scenario
= Scenario Spec
type alias Spec =
{ validator : String
, purpose : Purpose
, redeemer : Data
, parameters : List Data
, signers : List PubKeyHash
, inputs : List Input
, referenceInputs : List Input
, outputs : List Output
, minted : List Token
, fee : Int
, validFrom : Maybe Int
, validBefore : Maybe Int
, ownLovelace : Int
, expectSuccess : Bool
, budget : Maybe ( Int, Int )
}
type Purpose
= Spend TxOutRef (Maybe Data)
| Mint
| Withdraw
| Certify Int TxCert
| Vote Voter
| Propose Int ProposalProcedure
type Policy
= Own
| Policy Bytes
type alias Token =
{ policy : Policy
, name : Bytes
, quantity : Int
}
type Destination
= ToScript
| ToAddress Address
| ToStakedScript StakingCredential
type alias Output =
{ destination : Destination
, lovelace : Int
, tokens : List Token
, datum : OutputDatum
, hashedDatum : Maybe Data
}
type alias Input =
{ ref : TxOutRef
, output : Output
}

Policy is the policy of a token: Own is the validator’s own script hash, Policy bytes any other policy id. Destination is where an output is paid: the validator’s own address, any address, or the validator’s own payment credential with a staking part. An output’s datum is its inline datum or none; hashedDatum holds a datum by hash, which the runner turns into the datum’s hash on the output and the datum among the transaction’s witnesses. The ledger types TxOutRef, TxCert, Voter, ProposalProcedure, Address, StakingCredential, OutputDatum and PubKeyHash come from Julc.Ledger. Build scenarios with the functions below rather than the Spec record.

The first argument is the name of a [validators.<name>] table.

FunctionSignatureTransaction
spendString -> TxOutRef -> Data -> Scenariospends the output at ref, held at the validator’s address with an inline datum
spendWithoutDatumString -> TxOutRef -> Scenariospends the output at ref, held at the validator’s address without a datum
mintString -> Scenariomints or burns under the validator’s own policy; add tokens with minting
withdrawString -> Scenariowithdraws from the validator’s own staking credential
certifyString -> Int -> TxCert -> Scenariopublishes a certificate at an index
voteString -> Voter -> Scenariovotes as the given voter
proposeString -> Int -> ProposalProcedure -> Scenariosubmits a proposal at an index

A new scenario has these defaults:

FieldDefault
redeemerunit, the Data of ()
parameters, signers, inputs, reference inputs, outputs, minted tokensnone
fee200000 lovelace
validity rangeunbounded on both sides
lovelace in the spent output2000000
expectationthe validator accepts; no budget limit

Each function takes the scenario last, so they chain with |>. Functions that set a value replace any earlier value; functions that add to a list append in call order.

FunctionSignatureEffect
redeemerData -> Scenario -> Scenariosets the redeemer
parametersList Data -> Scenario -> Scenariosets the deployment parameters, in the validator’s declared order; all of them are required
signedByPubKeyHash -> Scenario -> Scenarioadds a required signer
inputTxOutRef -> Output -> Scenario -> Scenarioadds an input; an input at the spent reference replaces the default spent output, and must hold the datum the spend gives
referenceInputTxOutRef -> Output -> Scenario -> Scenarioadds a reference input
outputOutput -> Scenario -> Scenarioadds an output
mintingBytes -> Int -> Scenario -> Scenariomints (positive quantity) or burns (negative) a token under the own policy
feeInt -> Scenario -> Scenariosets the fee in lovelace
validFromInt -> Scenario -> Scenariosets the inclusive lower bound of the validity range, in POSIX time
validBeforeInt -> Scenario -> Scenariosets the exclusive upper bound of the validity range, in POSIX time
worthInt -> Scenario -> Scenariosets the lovelace held by the default spent output
FunctionSignatureEffect
expectFailureScenario -> Scenariothe validator must reject the transaction
withinBudgetInt -> Int -> Scenario -> Scenariothe execution must cost at most this many CPU steps and memory units, whether it succeeds or fails
FunctionSignatureResult
toScriptInt -> Outputan output of this many lovelace to the validator’s own address
toStakedScriptStakingCredential -> Int -> Outputan output of this many lovelace to the validator’s own payment credential with a staking part, an address other than toScript’s
toAddressAddress -> Int -> Outputan output of this many lovelace to an address
toKeyPubKeyHash -> Int -> Outputan output of this many lovelace to the key’s address, without a staking part
withDatumData -> Output -> Outputthe output with an inline datum; replaces a datum set before
withDatumHashData -> Output -> Outputthe output with the datum’s hash, and the datum among the transaction’s witnesses, where Tx.datumOf finds it; replaces a datum set before
withTokenPolicy -> Bytes -> Int -> Output -> Outputthe output with an added token: policy, name and quantity
FunctionSignatureResult
keyString -> PubKeyHashthe BLAKE2b-224 hash of the name’s UTF-8 bytes: a deterministic 28-byte test key
outRefString -> Int -> TxOutRefan output reference whose transaction id is the BLAKE2b-256 hash of the name’s UTF-8 bytes
unitDatathe Data of (), Constr 0 []
ReportMeaning
validator <name> rejected the transaction: <error>the validator failed, and the scenario expected success
validator <name> accepted a transaction expected to failthe validator succeeded after expectFailure
Budget exceeded: ...the execution went over the withinBudget limits
cannot prepare validator '<name>': ...the validator does not exist, or the scenario does not give all of its parameters; the scenario fails before it runs, even with expectFailure
cannot build the transaction: ...the ledger could not produce the scenario’s transaction, such as one with a token name longer than 32 bytes, or a listed spent output whose datum differs from the one spend gives; the scenario fails before it runs, even with expectFailure
import Property exposing (Property)
type Property

Property is opaque: build it with the functions below.

FunctionSignatureThe property holds when
holdsBool -> Propertythe condition is True; otherwise it fails with the property does not hold
expectExpectation -> Propertythe expectation passes; otherwise it fails with the expectation’s report
scenarioScenario -> Propertythe scenario has its expected outcome: accepted, or rejected after Scenario.expectFailure

A passing property reports its number of runs and the budget of its costliest run, as (100 runs, max cpu ..., mem ...). A failing property reports the runs until the failure and the shrunk counterexample, printed as a call in Elci syntax, followed by the failure of that run:

FAIL PricingProperties.quotesNeverLoseValue (15 runs, cpu 5409589, mem 21823)
counterexample after 15 runs, shrunk 4 times:
quotesNeverLoseValue (-334)
the property does not hold

A property’s parameters must be concrete, Data-encodable types. Functions, arrays, ScriptContext and type variables are rejected with ELCI2009. Property-based tests describes the generated values for each type.