Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.canton.network/llms.txt

Use this file to discover all available pages before exploring further.

Test Daml contracts

This section is about testing and debugging the Daml contracts you’ve built using the tools from earlier chapters. You’ve already met Daml Script as a way of testing your code inside the IDE. In this chapter you’ll learn about more ways to run Daml Scripts, as well as other tools you can use for testing and debugging. Specifically we will cover:
  • Running Daml Scripts to test and debug templates
  • The trace and debug functions
  • The choice coverage
Note that this section only covers testing your Daml contracts.
Remember that you can load all the code for this section into a folder called intro-test by running dpm new intro-test --template daml-intro-test

Multi-package project structure

In the project structures section you learned that it is important to keep the scripts and templates in separate packages. The project for this section is a multi-package build, containing all the code from compose and dependencies. The folder structure looks like this:
.
├── asset
├── asset-tests
├── multi-trade
├── multi-trade-tests
└── multi-package.yaml
asset, asset-tests, multi-trade and multi-trade-tests are packages. They each contain there own daml.yaml file. asset contains the code for the Asset and Trade templates. The scripts for testing Asset and Trade are in the asset-tests package, which depends on asset. Contrary to asset-tests, asset is meant to be uploaded to a Canton ledger. Similarly multi-trade contains the MultiTrade template, and multi-trade-tests contains the corresponding scripts. multi-package.yaml is the multi-package configuration. It allows dpm to orchestrate the build of each sub-package. It contains the list of sub-packages:
packages:
- ./asset
- ./asset-tests
- ./multi-trade
- ./multi-trade-tests
Run dpm build --all at the root of the multi-package project, to build all sub-packages. Or you can run dpm build in any of the sub-package. It detects the dependencies, and build them in the correct order. For instance, running dpm build in multi-trade-tests triggers the build of asset, multi-trade and multi-trade-tests in that order.

Test templates with Daml Scripts

Daml Script is the main tool to test Daml contracts. In a script, you can submit commands and queries from multiple parties on a fresh, initially empty, ledger. There are two main ways to run a Daml Script: - Click the Script results lens in VS Code: it provides the table and transaction views, which are useful for debugging. - Run the dpm test command, which is useful for regression checks and coverage.

Run a script in VS Code

In the earlier testing with scripts section, you learned how to write and run a Daml Script in VS Code. As a refresher, find a script in the asset-tests or multi-trade-tests and click the Script results lens, that appears on top of the script name. VS Code should open the table view in a side pane. The table view describes the final state of the ledger at the end of the script. It shows the list of active contracts, their data, and for each party, if it can see the contract or not. Click the Show archived toggle to see the list of archived contracts. In the side pane, click the Show transaction view button to switch to the transaction view. It shows you all the transactions, and sub-transactions, executed by the script. It also contain the failure message whenever a script fail. Try to change the submitting party of an exerciseCmd and see if the script is still succeeding. You can use the table and transaction views in VS Code to better understand the visibility of each contract, and authority of each party.

Run all scripts with dpm

dpm can run all the scripts inside a package. This is useful for quick regression check, and their automation in the CI. Open a terminal in the multi-trade-tests folder and run dpm test. It should succeed and print the following test summary:
Test Summary

daml/Intro/Asset/MultiTradeTests.daml:testMultiTrade: ok, 12 active contracts, 28 transactions.
daml/Intro/Asset/TradeSetup.daml:setupRoles: ok, 2 active contracts, 4 transactions.
daml/Intro/Asset/TradeSetup.daml:test_issuance: ok, 3 active contracts, 5 transactions.
daml/Intro/Asset/TradeSetup.daml:tradeSetup: ok, 6 active contracts, 10 transactions.
Modules internal to this package:
- Internal templates
  0 defined
  0 (100.0%) created
- Internal template choices
  0 defined
  0 (100.0%) exercised
- Internal interface implementations
  0 defined
    0 internal interfaces
    0 external interfaces
- Internal interface choices
  0 defined
  0 (100.0%) exercised
Modules external to this package:
- External templates
  7 defined
  5 ( 71.4%) created in any tests
  5 ( 71.4%) created in internal tests
  0 (  0.0%) created in external tests
- External template choices
  27 defined
  7 ( 25.9%) exercised in any tests
  7 ( 25.9%) exercised in internal tests
  0 (  0.0%) exercised in external tests
- External interface implementations
  0 defined
- External interface choices
  0 defined
  0 (100.0%) exercised in any tests
  0 (100.0%) exercised in internal tests
  0 (100.0%) exercised in external tests
The first part of the summary is a list of each executed script. For each script, you can see the total number of transactions, and active contracts at the end of the script. For instance the testMultiTrade executed 28 transactions, to create 12 active contracts. The second part of the summary is the coverage report. It shows you how many templates and choices are tested by the complete set of scripts in the package, in proportion of the total number of templates and choices. To learn more about Daml test coverage, read the Daml test coverage guide.

Debug, trace, and stacktraces

The above demonstrates nicely how to test the happy path, but what if a function doesn’t behave as you expected? Daml has two functions that allow you to do fine-grained printf debugging: debug and trace. Both allow you to print something to StdOut if the code is reached. The difference between debug and trace is similar to the relationship between abort and error:
  • debug : Text -> m () maps a text to an Action that has the side-effect of printing to StdOut.
  • trace : Text -> a -> a prints to StdOut when the expression is evaluated.
daml> let a : Script () = debug "foo"
daml> let b : Script () = trace "bar" (debug "baz")
[Daml.Script:378]: "bar"
daml> a
[DA.Internal.Prelude:532]: "foo"
daml> b
[DA.Internal.Prelude:532]: "baz"
daml>
If in doubt, use debug. It’s the easier of the two to interpret the results of. The thing in the square brackets is the last location. It’ll tell you the Daml file and line number that triggered the printing, but often no more than that because full stacktraces could violate subtransaction privacy quite easily. If you want to enable stacktraces for some purely functional code in your modules, you can use the machinery in the DA.Stack module to do so, but we won’t cover that any further here.

Local testing with dpm sandbox

For integration testing beyond Daml Script, you can run a local Canton sandbox using dpm sandbox. This starts a local participant node with an in-memory ledger, letting you test your contracts in a more realistic environment before deploying to DevNet or TestNet.
dpm sandbox
Once the sandbox is running, you can interact with it through the Ledger API (gRPC or JSON), submit commands from codegen-based Java or TypeScript clients, and test multi-party workflows with real API authentication.

When to use sandbox vs Daml Script

Use Daml Script for testing contract logic in isolation: authorization rules, choice behavior, multi-party workflows, and edge cases. Scripts run in-process without a ledger, making them fast and deterministic. They are the right tool for unit-testing your Daml model. Use the sandbox when you need to test how your application integrates with the ledger. Situations where the sandbox is the better choice:
  • Testing your Java or TypeScript backend against the Ledger API
  • Verifying that codegen-generated types serialize and deserialize correctly
  • Testing JSON API interactions from a frontend
  • Running multi-participant configurations
  • Testing DAR upload, party allocation, and user management through the Admin API
  • Validating authorization token handling in your application

When to use a local Canton Network

The cn-quickstart project includes a LocalNet configuration that runs a full Canton Network locally (participant, sequencer, mediator, and Splice applications). Use LocalNet instead of the sandbox when you need to test:
  • Canton Coin transfers and traffic purchases
  • Wallet SDK integration
  • Splice API interactions (Scan, Validator APIs)
  • End-to-end application flows that involve the Global Synchronizer
See the QuickStart walkthrough for instructions on starting LocalNet.