Skip to content
On this page

Testing contracts

Registering and displaying contracts

python
@sp.add_test(name = "A Test")
def test():
    # Create a scenario
    scenario = sp.test_scenario()
    # Instantiate a contract
    c1 = MyContract()

    # Add the contract to the scenario
    scenario += c1 # which is equivalent to `scenario.register(c1, show = True)`

    # To only register the smart contract but not show it
    scenario.register(c1)

Contract origination options

Initial storage

Contract storages are typically determined in the contract constructors but it's possible to do it right before registering them inside scenarios.

python
c2 = MyContract()
    c2.init_storage(sp.record(a = 12, b = True))
    scenario += c2

Initial balance

Additionally to entrypoints, contracts have an additional method that can be called once, before origination.

python
c1.set_initial_balance(expression)

Test accounts

Test accounts can be defined by calling sp.test_account(seed) where seed is a string.

A test account contains some fields:

  • <account>.address
  • <account>.public_key_hash
  • <account>.public_key
  • <account>.secret_key

See Cryptography

python
admin = sp.test_account("Administrator")
alice = sp.test_account("Alice")
bob   = sp.test_account("Robert")

Calls to entrypoints

python
# Call entrypoint
c1.my_entrypoint(12)
# Call entrypoint with customized block attributes.
c1.my_entrypoint(13).run(
    sender          = None, # sp.address
    source          = None, # sp.address
    chain_id        = None, # sp.chain_id
    level           = None, # sp.nait
    now             = None, # sp.timestamp
    voting_powers   = None, # sp.map[sp.key_hash,sp.nat]
    amount          = None, # sp.mutez
    valid           = True, # sp.bool
    show            = True, # sp.bool
    exception       = None, # any
)

The .run(...) method and its parameters are all optional.

Calls optional arguments

Generic context arguments are:

ParameterTypeAccessorDescription
sendersp.address or sp.test_accountsp.senderThe simulated sender of the transaction.
Specific to call.
sourcesp.address or sp.test_accountsp.sourceThe simulated source of the transaction.
Specific to call.
chain_idsp.chain_idsp.chain_idThe simulated chain_id.
Preserved until changed.
level[sp.nat]sp.levelThe simulated block level.
Preserved until changed.
now[sp.timestamp]sp.nowThe simulated block timestamp.
Preserved until changed.
voting_powers[sp.map][sp.key_hash, [sp.nat]]sp.total_voting_power,
sp.voting_power
The simulated voting powers for the test.
Preserved until changed.

Specific context arguments are:

ParameterTypeDescription
amount[sp.mutez]The simulated amount sent. It populates sp.amount.
valid[sp.bool]Tells the interpreter if the transaction is expected to fail or not. True by default.
show[sp.bool]Show or hide the transaction. True by default.
exceptionany typeThe expected exception raised by the transaction. If present, valid must be False.

Exceptions

Content of exceptions

Views

Views, both off-chain or on-chain, can now be called from test scenarios the same way as entrypoints. The example below shows how to do it.

Example

python
import smartpy as sp

class MyContract(sp.Contract):
    def __init__(self, param):
        self.init(param)

    @sp.offchain_view()
    def state(self, param):
        sp.verify(param < 5, "This is false: param > 5")
        sp.result(self.data * param)

@sp.add_test(name = "Minimal")
def test():
    scenario = sp.test_scenario()
    c1 = MyContract(1)
    scenario += c1

    """ Test views """

    # Display the offchain call result
    scenario.show(c1.state(1))

    # Assert the view result
    scenario.verify(c1.state(2) == 2)
    # Assert call failures
    scenario.verify(sp.is_failing(c1.state(6)));    # Expected to fail
    scenario.verify(~ sp.is_failing(c1.state(1)));   # Not expected to fail

    # Assert exception result
    # catch_exception returns an option:
    #      sp.none if the call succeeds
    #      sp.some(<exception>) if the call fails
    e = sp.catch_exception(c1.state(7), t = sp.string)
    scenario.verify(e == sp.some("This is false: param > 5"))

Document extra information

The following elements represent six levels of section headings.

<h1> is the highest section level and <p> is the lowest.

python
scenario.h1("a title")
scenario.h2("a subtitle")
scenario.h3('Equivalent to <h3> HTML tag.')
scenario.h4("Equivalent to <h4> HTML tag.")
scenario.p("Equivalent to <p> HTML tag.")

Expressions

Showing expressions

Computing expressions

Compute optional arguments

Generic context arguments are:

ParameterTypeAccessorDescription
sendersp.address or sp.test_accountsp.senderThe simulated sender of the computation.
Specific to computation.
sourcesp.address or sp.test_accountsp.sourceThe simulated source of the computation.
Specific to computation.
chain_idsp.chain_idsp.chain_idThe simulated chain_id.
Preserved until changed.
level[sp.nat]sp.levelThe simulated block level.
Preserved until changed.
now[sp.timestamp]sp.nowThe simulated block timestamp.
Preserved until changed.
voting_powers[sp.map][sp.key_hash, [sp.nat]]sp.total_voting_power,
sp.voting_power
The simulated voting powers for the test.
Preserved until changed.

Data associated with contracts

When a variable c1 represents a contract in a scenario, we can access some associated data:

  • c1.data

    Retrieve contract storage.

  • c1.balance

    Retrieve contract balance.

  • c1.baker

    Retrieve its optional delegated baker.

  • c1.address

    Retrieve its address within the scenario.

    In storage or similar circumstances, deployed contracts get addresses of the form:

    • KT1TezoooozzSmartPyzzSTATiCzzzwwBFA1
    • KT1Tezooo1zzSmartPyzzSTATiCzzzyfC8eF
    • KT1Tezooo2zzSmartPyzzSTATiCzzzwqqQ4H
    • KT1Tezooo3zzSmartPyzzSTATiCzzzseJjWC
    • KT1Tezooo4zzSmartPyzzSTATiCzzzyPVdv3
    • KT1Tezooo5zzSmartPyzzSTATiCzzzz48Z4p
    • KT1Tezooo6zzSmartPyzzSTATiCzzztY1196
    • KT1Tezooo7zzSmartPyzzSTATiCzzzvTbG1z
    • KT1Tezooo8zzSmartPyzzSTATiCzzzzp29d1
    • KT1Tezooo9zzSmartPyzzSTATiCzzztdBMLX
    • KT1Tezoo1ozzSmartPyzzSTATiCzzzw8CmuY
    • ...
  • c1.typed

    Retrieve its testing typed contract value.

    To access entrypoints, one can use field notation:

    • c1.typed.my_entrypoint: to access typed entrypoint my_entrypoint of contract c1.

Dynamic contracts

See reference Create Contract template.

Internally, SmartPy uses two types of contracts:

  • Static contracts which appear explicitly in the scenarios.
  • Dynamic contacts which are created in other contracts executed in the scenario (with sp.create_contract).

Declaring a dynamic contract of dynamic id (an integer) with the corresponding storage and full parameter types. The first dynamically created contractId is 0, then 1, etc.

python
dynamic_contract = scenario.dynamic_contract(contractId, tcontract, tparameter)

Return a dynamic contract that contains regular fields data, balance, baker, address, typed and a call(...) method.

Dynamic contracts addresses are of the form:

  • KT1TezoooozzSmartPyzzDYNAMiCzzpLu4LU
  • KT1Tezooo1zzSmartPyzzDYNAMiCzztcr8AZ
  • KT1Tezooo2zzSmartPyzzDYNAMiCzzxyHfG9
  • KT1Tezooo3zzSmartPyzzDYNAMiCzzvqsJQk
  • KT1Tezooo4zzSmartPyzzDYNAMiCzzywTMhC
  • KT1Tezooo5zzSmartPyzzDYNAMiCzzvwBH3X
  • KT1Tezooo6zzSmartPyzzDYNAMiCzzvyu5w3
  • KT1Tezooo7zzSmartPyzzDYNAMiCzztDqbVQ
  • KT1Tezooo8zzSmartPyzzDYNAMiCzzq2URWu
  • KT1Tezooo9zzSmartPyzzDYNAMiCzzwMosaF
  • KT1Tezoo1ozzSmartPyzzDYNAMiCzzzknqsi

Call method

Send the parameter to the dynamic contract entrypoint.

It is also possible to use .run(...) on the generated call as described in Registering and Displaying Calls to Entrypoints.

python
dynamic_contract.call(entrypoint, parameter)
dynamic_contract.call(entrypoint, parameter).run(
    sender          = ..., # sp.address
    source          = ..., # sp.address
    amount          = ..., # sp.mutez
    now             = ..., # sp.timestamp
    level           = ..., # sp.nat
    chain_id        = ..., # sp.chain_id
    voting_powers   = ..., # sp.map[sp.key_hash, sp.nat]
    valid           = ..., # sp.bool
    show            = ..., # sp.bool
    exception       = ..., # any
)

Assertions

Interactive testing