The LIGO command-line interpreter provides sub-commands to directly test your LIGO code. The three main sub-commands we currently support are:
We will show how to use the first two, while an example on how to use the third one was already explained here.
test can be used to test a contract using LIGO.
⚠️ Please keep in mind that this sub-command is still BETA, and that there are features that are work in progress and are subject to change. No real test procedure should rely on this sub-command alone.
When running the
test sub-command, LIGO code has access to an
Test module. This module provides ways of originating
contracts and executing transactions, as well as additional helper
functions that allow to control different parameters of the Tezos
Note: the LIGO interpreter uses the same library that Tezos internally uses for testing.
Test.originate allows to deploy a contract in the
testing environment. It takes a contract, which is represented as a
function of type
'parameter * 'storage -> operation list * 'storage,
an initial storage of type
'storage, and an initial balance for the
contract being deployed. This function deploys the contract, and
returns the type
('parameter, 'storage) typed_address, the compiled program in
Michelson of type
michelson_program, and the size of the program of
The storage of a deployed contract can be queried using the
Test.get_storage function, that given a typed address
'storage) typed_address, returns the
As a concrete example, suppose we have the following contract:
We can deploy it and query the storage right after, to check that the storage is in fact the one which we started with:
The test sub-command will evaluate all top-level definitions and print any
entries that begin with the prefix
test as well as the value that these
definitions evaluate to. If any of the definitions are found to have
failed, a message will be issued with the line number where the problem
Test.transfer_to_contract allows to bake a transaction.
It takes a target account of type
'parameter contract, the parameter
'parameter and an amount of type
tez. This function
performs the transaction, and returns a
tells whether the transaction was successful or not, and in case it
was not, it contains a
test_exec_error describing the error. There
is an alternative version, called
which performs the transaction and ignores the result, failing in case
that there was an error.
We can extend the previous example by executing a transaction that increments the storage after deployment:
The environment assumes a source for the operations which can be set
using the function
Test.set_source : address -> unit.
Consider a map binding addresses to amounts and a function removing all entries in that map having an amount less to a given threshold.
Let us imagine that we want to test this function against a range of thresholds with the LIGO test framework.
First, let's include the file under test and reset the state with 5 bootstrap accounts (we are going to use the bootstrap addresses later)
Now build the
balances map that will serve as the input of our test.
Our simple test loop will call
balances_under with the compiled map
defined above, get the size of the resulting map and compare it to an
expected value with
The call to
balance_under and the computation of the size of the resulting map is achieved through the primitive
This primitive runs a function on an input, translating both (function and input)
to Michelson before running on the Michelson interpreter.
Test.run f v performs the following:
- Compiles the function argument
- Compiles the value argument
v(which was already evaluated) to Michelson
- Runs the Michelson interpreter on the code
f_michwith the initial stack
[ v_mich ]
The function that is being compiled is called
We also print the actual and expected sizes for good measure.
You can now execute the test:
interpret allows to interpret an expression in a
context initialised by a source file. The interpretation is done using
We can see how it works on an example. Suppose we want to test the following contract.
This contract keeps an integer as storage, and has three entry-points:
one for incrementing the storage, one for decrementing the storage,
and one for resetting the storage to
As a simple property, we check whether starting with a storage of
10, if we execute the entry-point for incrementing
32, then we get
a resulting storage of
42. For checking it, we can interpret the
With the argument
--init-file we pass the contract we want to test,
and the sub-command requires also the expression to evaluate in that
context, in this case, a call to our contract (
main) with parameter
Increment (32) and storage
10. As a result, we can check that the
resulting storage is
42 (the second component of the pair), and
there are no further operations to execute (the first component).
We can tune certain parameters of the execution by passing them as arguments: