Testing using scenarios¶
Daml has a built-in mechanism for testing templates called scenarios.
Scenarios emulate the ledger. You can specify a linear sequence of actions that various parties take, and these are evaluated in order, according to the same consistency, authorization, and privacy rules as they would be on the sandbox ledger or ledger server. Daml Studio shows you the resulting Transaction graph.
For more on how scenarios work, see the Examples below.
Scenario syntax¶
Scenarios¶
example =
scenario do
A scenario
emulates the ledger, in order to test that a Daml template or sequence of templates are working as they should.
It consists of a sequence of transactions to be submitted to the ledger (after do
), together with success or failure assertions.
Transaction submission¶
-- Creates an instance of the Payout contract, authorized by "Alice"
submit alice do
The submit function attempts to submit a transaction to the ledger on behalf of a Party
.
For example, a transaction could be creating a contract on the ledger, or exercising a choice on an existing contract.
Asserting transaction failure¶
submitMustFail alice do
exercise payAlice Call
The submitMustFail function asserts that submitting a transaction to the ledger would fail.
This is essentially the same as submit
, except that the scenario tests that the action doesn’t work.
Full syntax¶
For detailed syntax, see Reference: scenarios.
Running scenarios in Daml Studio¶
When you load a file that includes scenarios into Daml Studio, it displays a “Scenario results” link above the scenario. Click the link to see a representation of the ledger after the scenario has run.
Examples¶
Simple example¶
A very simple scenario looks like this:
example =
scenario do
-- Creates the party Alice
alice <- getParty "Alice"
-- Creates an instance of the Payout contract, authorized by "Alice"
submit alice do
create Payout
-- There’s only one party: "Alice" is both the receiver and giver.
with receiver = alice; giver = alice
In this example, there is only one transaction, authorized by the party Alice
(created using getParty "Alice"
). The ledger update is a create
, and has to include the arguments for the template (Payout with receiver = alice; giver = alice
).
Example with two updates¶
This example tests a contract that gives both parties an explicit opportunity to agree to their obligations.
example =
scenario do
-- Bank of England creates a contract giving Alice the option
-- to be paid.
bankOfEngland <- getParty "Bank of England"
alice <- getParty "Alice"
payAlice <- submit bankOfEngland do
create CallablePayout with
receiver = alice; giver = bankOfEngland
-- Alice exercises the contract, and receives payment.
submit alice do
exercise payAlice Call
In the first transaction of the scenario, party bankOfEngland
(created using getParty "Bank of England"
) creates a CallablePayout
contract with alice
as the receiver and bankOfEngland
as the giver.
When the contract is submitted to the ledger, it is given a unique contract identifier of type ContractId CallablePayout
. payAlice <-
assigns that identifier to the variable payAlice
.
In the second statement, exercise payAlice Call
, is an exercise of the Call
choice on the contract identified by payAlice
. This causes a Payout
agreement with her as the receiver
to be written to the ledger.
The workflow described by the above scenario models both parties explicitly exercising their rights and accepting their obligations:
- Party
"Bank of England"
is assumed to know the definition of theCallablePayout
contract template and the consequences of submitting a contract to the ledger. - Party
"Alice"
is assumed to know the definition of the contract template, as well as the consequences of exercising theCall
choice on it. If"Alice"
does not want to receive five pounds, she can simply not exercise that choice.
Example with submitMustFail¶
Because exercising a contract (by default) archives a contract, once party "Alice"
exercises the Call
choice, she will be unable to exercise it again.
To test this expectation, use the submitMustFail
function:
exampleDoubleCall =
scenario do
bankOfEngland <- getParty "Bank of England"
alice <- getParty "Alice"
-- Bank of England creates a contract giving Alice the option
-- to be paid.
payAlice <- submit bankOfEngland do
create CallablePayout with
receiver = alice; giver = bankOfEngland
-- Alice exercises the contract, and receives payment.
submit alice do
exercise payAlice Call
-- If Alice tries to exercise the contract again, it must
-- fail.
submitMustFail alice do
exercise payAlice Call
When the Call
choice is exercised, the contract is archived. The fails
keyword checks that if 'Alice'
submits exercise payAlice Call
again, it would fail.