Skip to main content
Version: 1.0

Cadence Testing Framework

The Cadence testing framework provides a convenient way to write tests for Cadence programs in Cadence. This functionality is provided by the built-in Test contract.

info

The testing framework can only be used off-chain, e.g. by using the Flow CLI.

Tests must be written in the form of a Cadence script. A test script may contain testing functions that starts with the test prefix, a setup function that always runs before the tests, a tearDown function that always runs at the end of all test cases, a beforeEach function that runs before each test case, and an afterEach function that runs after each test case. All the above four functions are optional.


_34
// A `setup` function that always runs before the rest of the test cases.
_34
// Can be used to initialize things that would be used across the test cases.
_34
// e.g: initialling a blockchain backend, initializing a contract, etc.
_34
access(all) fun setup() {
_34
}
_34
_34
// The `beforeEach` function runs before each test case. Can be used to perform
_34
// some state cleanup before each test case, among other things.
_34
access(all) fun beforeEach() {
_34
}
_34
_34
// The `afterEach` function runs after each test case. Can be used to perform
_34
// some state cleanup after each test case, among other things.
_34
access(all) fun afterEach() {
_34
}
_34
_34
// Valid test functions start with the 'test' prefix.
_34
access(all) fun testSomething() {
_34
}
_34
_34
access(all) fun testAnotherThing() {
_34
}
_34
_34
access(all) fun testMoreThings() {
_34
}
_34
_34
// Test functions cannot have any arguments or return values.
_34
access(all) fun testInvalidSignature(message: String): Bool {
_34
}
_34
_34
// A `tearDown` function that always runs at the end of all test cases.
_34
// e.g: Can be used to stop the blockchain back-end used for tests, etc. or any cleanup.
_34
access(all) fun tearDown() {
_34
}

Test Standard Library

The testing framework can be used by importing the built-in Test contract:


_10
import Test

Assertions

Test.assert


_10
fun assert(_ condition: Bool, message: String)

Fails a test-case if the given condition is false, and reports a message which explains why the condition is false.

The message argument is optional.


_10
import Test
_10
_10
access(all) fun testExample() {
_10
Test.assert(2 == 2)
_10
Test.assert([1, 2, 3].length == 0, message: "Array length is not 0")
_10
}

Test.fail


_10
fun fail(message: String)

Immediately fails a test-case, with a message explaining the reason to fail the test.

The message argument is optional.


_10
import Test
_10
_10
access(all) fun testExample() {
_10
let array = [1, 2, 3]
_10
_10
if array.length != 0 {
_10
Test.fail(message: "Array length is not 0")
_10
}
_10
}

Test.expect


_10
fun expect(_ value: AnyStruct, _ matcher: Matcher)

The expect function tests a value against a matcher (see matchers section), and fails the test if it's not a match.


_10
import Test
_10
_10
access(all) fun testExample() {
_10
let array = [1, 2, 3]
_10
_10
Test.expect(array.length, Test.equal(3))
_10
}

Test.assertEqual


_10
fun assertEqual(_ expected: AnyStruct, _ actual: AnyStruct)

The assertEqual function fails the test-case if the given values are not equal, and reports a message which explains how the two values differ.


_36
import Test
_36
_36
access(all) struct Foo {
_36
access(all) let answer: Int
_36
_36
init(answer: Int) {
_36
self.answer = answer
_36
}
_36
}
_36
_36
access(all) fun testExample() {
_36
Test.assertEqual("this string", "this string")
_36
Test.assertEqual(21, 21)
_36
Test.assertEqual(true, true)
_36
Test.assertEqual([1, 2, 3], [1, 2, 3])
_36
Test.assertEqual(
_36
{1: true, 2: false, 3: true},
_36
{1: true, 2: false, 3: true}
_36
)
_36
_36
let address1 = Address(0xf8d6e0586b0a20c7)
_36
let address2 = Address(0xf8d6e0586b0a20c7)
_36
Test.assertEqual(address1, address2)
_36
_36
let foo1 = Foo(answer: 42)
_36
let foo2 = Foo(answer: 42)
_36
_36
Test.assertEqual(foo1, foo2)
_36
_36
let number1: Int64 = 100
_36
let number2: UInt64 = 100
_36
// Note that the two values need to have exactly the same type,
_36
// and not just value, otherwise the assertion fails:
_36
// assertion failed: not equal: expected: 100, actual: 100
_36
Test.assertEqual(number1, number2)
_36
}

Test.expectFailure


_10
fun expectFailure(_ functionWrapper: ((): Void), errorMessageSubstring: String)

The expectFailure function wraps a function call in a closure, and expects it to fail with an error message that contains the given error message portion.


_24
import Test
_24
_24
access(all) struct Foo {
_24
access(self) let answer: UInt8
_24
_24
init(answer: UInt8) {
_24
self.answer = answer
_24
}
_24
_24
access(all) fun correctAnswer(_ input: UInt8): Bool {
_24
if self.answer != input {
_24
panic("wrong answer!")
_24
}
_24
return true
_24
}
_24
}
_24
_24
access(all) fun testExample() {
_24
let foo = Foo(answer: 42)
_24
_24
Test.expectFailure(fun(): Void {
_24
foo.correctAnswer(43)
_24
}, errorMessageSubstring: "wrong answer!")
_24
}

Matchers

A matcher is an object that consists of a test function and associated utility functionality.


_26
access(all) struct Matcher {
_26
_26
access(all) let test: fun(AnyStruct): Bool
_26
_26
access(all) init(test: fun(AnyStruct): Bool) {
_26
self.test = test
_26
}
_26
_26
/// Combine this matcher with the given matcher.
_26
/// Returns a new matcher that succeeds if this and the given matcher succeed.
_26
///
_26
access(all) fun and(_ other: Matcher): Matcher {
_26
return Matcher(test: fun (value: AnyStruct): Bool {
_26
return self.test(value) && other.test(value)
_26
})
_26
}
_26
_26
/// Combine this matcher with the given matcher.
_26
/// Returns a new matcher that succeeds if this or the given matcher succeeds.
_26
///
_26
access(all) fun or(_ other: Matcher): Matcher {
_26
return Matcher(test: fun (value: AnyStruct): Bool {
_26
return self.test(value) || other.test(value)
_26
})
_26
}
_26
}

The test function defines the evaluation criteria for a value, and returns a boolean indicating whether the value conforms to the test criteria defined in the function.

The and and or functions can be used to combine this matcher with another matcher to produce a new matcher with multiple testing criteria. The and method returns a new matcher that succeeds if both this and the given matcher are succeeded. The or method returns a new matcher that succeeds if at-least this or the given matcher is succeeded.

A matcher that accepts a generic-typed test function can be constructed using the newMatcher function.


_10
fun newMatcher<T: AnyStruct>(_ test: fun(T): Bool): Test.Matcher

The type parameter T is bound to AnyStruct type. It is also optional.

For example, a matcher that checks whether a given integer value is negative can be defined as follows:


_31
import Test
_31
_31
access(all) fun testExample() {
_31
let isNegative = Test.newMatcher(fun (_ value: Int): Bool {
_31
return value < 0
_31
})
_31
_31
Test.expect(-15, isNegative)
_31
// Alternatively, we can use `Test.assert` and the matcher's `test` function.
_31
Test.assert(isNegative.test(-15), message: "number is not negative")
_31
}
_31
_31
access(all) fun testCustomMatcherUntyped() {
_31
let matcher = Test.newMatcher(fun (_ value: AnyStruct): Bool {
_31
if !value.getType().isSubtype(of: Type<Int>()) {
_31
return false
_31
}
_31
_31
return (value as! Int) > 5
_31
})
_31
_31
Test.expect(8, matcher)
_31
}
_31
_31
access(all) fun testCustomMatcherTyped() {
_31
let matcher = Test.newMatcher<Int>(fun (_ value: Int): Bool {
_31
return value == 7
_31
})
_31
_31
Test.expect(7, matcher)
_31
}

The Test contract provides some built-in matcher functions for convenience.

Test.equal


_10
fun equal(_ value: AnyStruct): Matcher

The equal function returns a matcher that succeeds if the tested value is equal to the given value. Accepts an AnyStruct value.


_10
import Test
_10
_10
access(all) fun testExample() {
_10
let array = [1, 2, 3]
_10
_10
Test.expect([1, 2, 3], Test.equal(array))
_10
}

Test.beGreaterThan


_10
fun beGreaterThan(_ value: Number): Matcher

The beGreaterThan function returns a matcher that succeeds if the tested value is a number and greater than the given number.


_10
import Test
_10
_10
access(all) fun testExample() {
_10
let str = "Hello, there"
_10
_10
Test.expect(str.length, Test.beGreaterThan(5))
_10
}

Test.beLessThan


_10
fun beLessThan(_ value: Number): Matcher

The beLessThan function returns a matcher that succeeds if the tested value is a number and less than the given number.


_10
import Test
_10
_10
access(all) fun testExample() {
_10
let str = "Hello, there"
_10
_10
Test.expect(str.length, Test.beLessThan(15))
_10
}

Test.beNil


_10
fun beNil(): Matcher

The beNil function returns a new matcher that checks if the given test value is nil.


_10
import Test
_10
_10
access(all) fun testExample() {
_10
let message: String? = nil
_10
_10
Test.expect(message, Test.beNil())
_10
}

Test.beEmpty


_10
fun beEmpty(): Matcher

The beEmpty function returns a matcher that succeeds if the tested value is an array or dictionary, and the tested value contains no elements.


_11
import Test
_11
_11
access(all) fun testExample() {
_11
let array: [String] = []
_11
_11
Test.expect(array, Test.beEmpty())
_11
_11
let dictionary: {String: String} = {}
_11
_11
Test.expect(dictionary, Test.beEmpty())
_11
}

Test.haveElementCount


_10
fun haveElementCount(_ count: Int): Matcher

The haveElementCount function returns a matcher that succeeds if the tested value is an array or dictionary, and has the given number of elements.


_11
import Test
_11
_11
access(all) fun testExample() {
_11
let array: [String] = ["one", "two", "three"]
_11
_11
Test.expect(array, Test.haveElementCount(3))
_11
_11
let dictionary: {String: Int} = {"one": 1, "two": 2, "three": 3}
_11
_11
Test.expect(dictionary, Test.haveElementCount(3))
_11
}

Test.contain


_10
fun contain(_ element: AnyStruct): Matcher

The contain function returns a matcher that succeeds if the tested value is an array that contains a value that is equal to the given value, or the tested value is a dictionary that contains an entry where the key is equal to the given value.


_10
access(all) fun testExample() {
_10
let array: [String] = ["one", "two", "three"]
_10
_10
Test.expect(array, Test.contain("one"))
_10
_10
let dictionary: {String: Int} = {"one": 1, "two": 2, "three": 3}
_10
_10
Test.expect(dictionary, Test.contain("two"))
_10
}

Test.beSucceeded


_10
fun beSucceeded(): Matcher

The beSucceeded function returns a new matcher that checks if the given test value is either a ScriptResult or TransactionResult and the ResultStatus is succeeded. Returns false in any other case.


_12
import Test
_12
_12
access(all) fun testExample() {
_12
let blockchain = Test.newEmulatorBlockchain()
_12
let result = blockchain.executeScript(
_12
"access(all) fun main(): Int { return 2 + 3 }",
_12
[]
_12
)
_12
_12
Test.expect(result, Test.beSucceeded())
_12
Test.assertEqual(5, result.returnValue! as! Int)
_12
}

Test.beFailed


_10
fun beFailed(): Matcher

The beFailed function returns a new matcher that checks if the given test value is either a ScriptResult or TransactionResult and the ResultStatus is failed. Returns false in any other case.


_17
import Test
_17
_17
access(all) fun testExample() {
_17
let blockchain = Test.newEmulatorBlockchain()
_17
let account = blockchain.createAccount()
_17
_17
let tx = Test.Transaction(
_17
code: "transaction { execute{ panic(\"some error\") } }",
_17
authorizers: [],
_17
signers: [account],
_17
arguments: [],
_17
)
_17
_17
let result = blockchain.executeTransaction(tx)
_17
_17
Test.expect(result, Test.beFailed())
_17
}

Matcher combinators

The built-in matchers, as well as custom matchers, can be combined with the three available combinators:

  • not,
  • or,
  • and

in order to create more elaborate matchers and increase re-usability.

not


_10
fun not(_ matcher: Matcher): Matcher

The not function returns a new matcher that negates the test of the given matcher.


_14
import Test
_14
_14
access(all) fun testExample() {
_14
let isEven = Test.newMatcher<Int>(fun (_ value: Int): Bool {
_14
return value % 2 == 0
_14
})
_14
_14
Test.expect(8, isEven)
_14
Test.expect(7, Test.not(isEven))
_14
_14
let isNotEmpty = Test.not(Test.beEmpty())
_14
_14
Test.expect([1, 2, 3], isNotEmpty)
_14
}

or


_10
fun or(_ other: Matcher): Matcher

The Matcher.or function combines this matcher with the given matcher. Returns a new matcher that succeeds if this or the given matcher succeed. If this matcher succeeds, then the other matcher would not be tested.


_10
import Test
_10
_10
access(all) fun testExample() {
_10
let one = Test.equal(1)
_10
let two = Test.equal(2)
_10
_10
let oneOrTwo = one.or(two)
_10
_10
Test.expect(2, oneOrTwo)
_10
}

and


_10
fun and(_ other: Matcher): Matcher

The Matcher.and function combines this matcher with the given matcher. Returns a new matcher that succeeds if this and the given matcher succeed.


_14
import Test
_14
_14
access(all) fun testExample() {
_14
let sevenOrMore = Test.newMatcher<Int>(fun (_ value: Int): Bool {
_14
return value >= 7
_14
})
_14
let lessThanTen = Test.newMatcher<Int>(fun (_ value: Int): Bool {
_14
return value <= 10
_14
})
_14
_14
let betweenSevenAndTen = sevenOrMore.and(lessThanTen)
_14
_14
Test.expect(8, betweenSevenAndTen)
_14
}

Blockchain

A blockchain is an environment to which transactions can be submitted to, and against which scripts can be run. It imitates the behavior of a real network, for testing.


_133
/// Blockchain emulates a real network.
_133
///
_133
access(all) struct Blockchain {
_133
_133
access(all) let backend: AnyStruct{BlockchainBackend}
_133
_133
init(backend: AnyStruct{BlockchainBackend}) {
_133
self.backend = backend
_133
}
_133
_133
/// Executes a script and returns the script return value and the status.
_133
/// `returnValue` field of the result will be `nil` if the script failed.
_133
///
_133
access(all) fun executeScript(_ script: String, _ arguments: [AnyStruct]): ScriptResult {
_133
return self.backend.executeScript(script, arguments)
_133
}
_133
_133
/// Creates a signer account by submitting an account creation transaction.
_133
/// The transaction is paid by the service account.
_133
/// The returned account can be used to sign and authorize transactions.
_133
///
_133
access(all) fun createAccount(): Account {
_133
return self.backend.createAccount()
_133
}
_133
_133
/// Add a transaction to the current block.
_133
///
_133
access(all) fun addTransaction(_ tx: Transaction) {
_133
self.backend.addTransaction(tx)
_133
}
_133
_133
/// Executes the next transaction in the block, if any.
_133
/// Returns the result of the transaction, or nil if no transaction was scheduled.
_133
///
_133
access(all) fun executeNextTransaction(): TransactionResult? {
_133
return self.backend.executeNextTransaction()
_133
}
_133
_133
/// Commit the current block.
_133
/// Committing will fail if there are un-executed transactions in the block.
_133
///
_133
access(all) fun commitBlock() {
_133
self.backend.commitBlock()
_133
}
_133
_133
/// Executes a given transaction and commits the current block.
_133
///
_133
access(all) fun executeTransaction(_ tx: Transaction): TransactionResult {
_133
self.addTransaction(tx)
_133
let txResult = self.executeNextTransaction()!
_133
self.commitBlock()
_133
return txResult
_133
}
_133
_133
/// Executes a given set of transactions and commits the current block.
_133
///
_133
access(all) fun executeTransactions(_ transactions: [Transaction]): [TransactionResult] {
_133
for tx in transactions {
_133
self.addTransaction(tx)
_133
}
_133
_133
var results: [TransactionResult] = []
_133
for tx in transactions {
_133
let txResult = self.executeNextTransaction()!
_133
results.append(txResult)
_133
}
_133
_133
self.commitBlock()
_133
return results
_133
}
_133
_133
/// Deploys a given contract, and initializes it with the arguments.
_133
///
_133
access(all) fun deployContract(
_133
name: String,
_133
code: String,
_133
account: Account,
_133
arguments: [AnyStruct]
_133
): Error? {
_133
return self.backend.deployContract(
_133
name: name,
_133
code: code,
_133
account: account,
_133
arguments: arguments
_133
)
_133
}
_133
_133
/// Set the configuration to be used by the blockchain.
_133
/// Overrides any existing configuration.
_133
///
_133
access(all) fun useConfiguration(_ configuration: Configuration) {
_133
self.backend.useConfiguration(configuration)
_133
}
_133
_133
/// Returns all the logs from the blockchain, up to the calling point.
_133
///
_133
access(all) fun logs(): [String] {
_133
return self.backend.logs()
_133
}
_133
_133
/// Returns the service account of the blockchain. Can be used to sign
_133
/// transactions with this account.
_133
///
_133
access(all) fun serviceAccount(): Account {
_133
return self.backend.serviceAccount()
_133
}
_133
_133
/// Returns all events emitted from the blockchain.
_133
///
_133
access(all) fun events(): [AnyStruct] {
_133
return self.backend.events(nil)
_133
}
_133
_133
/// Returns all events emitted from the blockchain,
_133
/// filtered by type.
_133
///
_133
access(all) fun eventsOfType(_ type: Type): [AnyStruct] {
_133
return self.backend.events(type)
_133
}
_133
_133
/// Resets the state of the blockchain to the given height.
_133
///
_133
access(all) fun reset(to height: UInt64) {
_133
self.backend.reset(to: height)
_133
}
_133
_133
/// Moves the time of the blockchain by the given delta,
_133
/// which should be passed in the form of seconds.
_133
///
_133
access(all) fun moveTime(by delta: Fix64) {
_133
self.backend.moveTime(by: delta)
_133
}
_133
}

The BlockchainBackend provides the actual functionality of the blockchain.


_33
/// BlockchainBackend is the interface to be implemented by the backend providers.
_33
///
_33
access(all) struct interface BlockchainBackend {
_33
_33
access(all) fun executeScript(_ script: String, _ arguments: [AnyStruct]): ScriptResult
_33
_33
access(all) fun createAccount(): Account
_33
_33
access(all) fun addTransaction(_ tx: Transaction)
_33
_33
access(all) fun executeNextTransaction(): TransactionResult?
_33
_33
access(all) fun commitBlock()
_33
_33
access(all) fun deployContract(
_33
name: String,
_33
code: String,
_33
account: Account,
_33
arguments: [AnyStruct]
_33
): Error?
_33
_33
access(all) fun useConfiguration(_ configuration: Configuration)
_33
_33
access(all) fun logs(): [String]
_33
_33
access(all) fun serviceAccount(): Account
_33
_33
access(all) fun events(_ type: Type?): [AnyStruct]
_33
_33
access(all) fun reset(to height: UInt64)
_33
_33
access(all) fun moveTime(by delta: Fix64)
_33
}

Creating a blockchain

A new blockchain instance can be created using the Test.newEmulatorBlockchain method. It returns a Blockchain which is backed by a new Flow Emulator instance.


_10
import Test
_10
_10
access(all) let blockchain = Test.newEmulatorBlockchain()

Creating accounts

It may be necessary to create accounts during tests for various reasons, such as for deploying contracts, signing transactions, etc. An account can be created using the createAccount function.


_10
import Test
_10
_10
access(all) let blockchain = Test.newEmulatorBlockchain()
_10
access(all) let account = blockchain.createAccount()
_10
_10
access(all) fun testExample() {
_10
log(account.address)
_10
}

Running the above command, from the command-line, we would get:


_10
flow test tests/test_sample_usage.cdc
_10
3:31PM DBG LOG: 0x01cf0e2f2f715450
_10
_10
Test results: "tests/test_sample_usage.cdc"
_10
- PASS: testExample

The returned account consists of the address of the account, and a publicKey associated with it.


_11
/// Account represents info about the account created on the blockchain.
_11
///
_11
access(all) struct Account {
_11
access(all) let address: Address
_11
access(all) let publicKey: PublicKey
_11
_11
init(address: Address, publicKey: PublicKey) {
_11
self.address = address
_11
self.publicKey = publicKey
_11
}
_11
}

Executing scripts

Scripts can be run with the executeScript function, which returns a ScriptResult. The function takes script-code as the first argument, and the script-arguments as an array as the second argument.


_19
import Test
_19
_19
access(all) let blockchain = Test.newEmulatorBlockchain()
_19
_19
access(all) fun testExample() {
_19
let code = "access(all) fun main(name: String): String { return \"Hello, \".concat(name) }"
_19
let args = ["Peter"]
_19
_19
let scriptResult = blockchain.executeScript(code, args)
_19
_19
// Assert that the script was successfully executed.
_19
Test.expect(scriptResult, Test.beSucceeded())
_19
_19
// returnValue has always the type `AnyStruct`,
_19
// so we need to type-cast accordingly.
_19
let returnValue = scriptResult.returnValue! as! String
_19
_19
Test.assertEqual("Hello, Peter", returnValue)
_19
}

The script result consists of the status of the script execution, and a returnValue if the script execution was successful, or an error otherwise (see errors section for more details on errors).


_13
/// The result of a script execution.
_13
///
_13
access(all) struct ScriptResult {
_13
access(all) let status: ResultStatus
_13
access(all) let returnValue: AnyStruct?
_13
access(all) let error: Error?
_13
_13
init(status: ResultStatus, returnValue: AnyStruct?, error: Error?) {
_13
self.status = status
_13
self.returnValue = returnValue
_13
self.error = error
_13
}
_13
}

Executing transactions

A transaction must be created with the transaction code, a list of authorizes, a list of signers that would sign the transaction, and the transaction arguments.


_15
/// Transaction that can be submitted and executed on the blockchain.
_15
///
_15
access(all) struct Transaction {
_15
access(all) let code: String
_15
access(all) let authorizers: [Address]
_15
access(all) let signers: [Account]
_15
access(all) let arguments: [AnyStruct]
_15
_15
init(code: String, authorizers: [Address], signers: [Account], arguments: [AnyStruct]) {
_15
self.code = code
_15
self.authorizers = authorizers
_15
self.signers = signers
_15
self.arguments = arguments
_15
}
_15
}

The number of authorizers must match the number of &Account parameters in the prepare block of the transaction.


_39
import Test
_39
_39
access(all) let blockchain = Test.newEmulatorBlockchain()
_39
access(all) let account = blockchain.createAccount()
_39
_39
// There are two ways to execute the created transaction.
_39
_39
access(all) fun testExample() {
_39
let tx = Test.Transaction(
_39
code: "transaction { prepare(acct: &Account) {} execute{} }",
_39
authorizers: [account.address],
_39
signers: [account],
_39
arguments: [],
_39
)
_39
_39
// Executing the transaction immediately
_39
// This may fail if the current block contains
_39
// transactions that have not being executed yet.
_39
let txResult = blockchain.executeTransaction(tx)
_39
_39
Test.expect(txResult, Test.beSucceeded())
_39
}
_39
_39
access(all) fun testExampleTwo() {
_39
let tx = Test.Transaction(
_39
code: "transaction { prepare(acct: &Account) {} execute{} }",
_39
authorizers: [account.address],
_39
signers: [account],
_39
arguments: [],
_39
)
_39
_39
// Add to the current block
_39
blockchain.addTransaction(tx)
_39
_39
// Execute the next transaction in the block
_39
let txResult = blockchain.executeNextTransaction()!
_39
_39
Test.expect(txResult, Test.beSucceeded())
_39
}

The result of a transaction consists of the status of the execution, and an Error if the transaction failed.


_11
/// The result of a transaction execution.
_11
///
_11
access(all) struct TransactionResult {
_11
access(all) let status: ResultStatus
_11
access(all) let error: Error?
_11
_11
init(status: ResultStatus, error: Error?) {
_11
self.status = status
_11
self.error = error
_11
}
_11
}

Commit block

commitBlock block will commit the current block, and will fail if there are any un-executed transactions in the block.


_20
import Test
_20
_20
access(all) let blockchain = Test.newEmulatorBlockchain()
_20
access(all) let account = blockchain.createAccount()
_20
_20
access(all) fun testExample() {
_20
let tx = Test.Transaction(
_20
code: "transaction { prepare(acct: &Account) {} execute{} }",
_20
authorizers: [account.address],
_20
signers: [account],
_20
arguments: [],
_20
)
_20
_20
blockchain.commitBlock()
_20
_20
blockchain.addTransaction(tx)
_20
_20
// This will fail with `error: internal error: pending block with ID 1f9...c0b7740d2 cannot be committed before execution`
_20
blockchain.commitBlock()
_20
}

Deploying contracts

A contract can be deployed using the deployContract function of the Blockchain.

Suppose we have this contract (Foo.cdc):


_11
access(all) contract Foo {
_11
access(all) let msg: String
_11
_11
init(_ msg: String) {
_11
self.msg = msg
_11
}
_11
_11
access(all) fun sayHello(): String {
_11
return self.msg
_11
}
_11
}


_16
import Test
_16
_16
access(all) let blockchain = Test.newEmulatorBlockchain()
_16
access(all) let account = blockchain.createAccount()
_16
_16
access(all) fun testExample() {
_16
let contractCode = Test.readFile("Foo.cdc")
_16
let err = blockchain.deployContract(
_16
name: "Foo",
_16
code: contractCode,
_16
account: account,
_16
arguments: ["hello from args"],
_16
)
_16
_16
Test.expect(err, Test.beNil())
_16
}

An Error is returned if the contract deployment fails. Otherwise, a nil is returned.

Configuring import addresses

A common pattern in Cadence projects is to define the imports as file locations and specify the addresses corresponding to each network in the Flow CLI configuration file. When writing tests for such a project, it may also require to specify the addresses to be used during the tests as well. However, during tests, since accounts are created dynamically and the addresses are also generated dynamically, specifying the addresses statically in a configuration file is not an option.

Hence, the test framework provides a way to specify the addresses using the useConfiguration(_ configuration: Test.Configuration) function in Blockchain.

The Configuration struct consists of a mapping of import locations to their addresses.


_10
/// Configuration to be used by the blockchain.
_10
/// Can be used to set the address mapping.
_10
///
_10
access(all) struct Configuration {
_10
access(all) let addresses: {String: Address}
_10
_10
init(addresses: {String: Address}) {
_10
self.addresses = addresses
_10
}
_10
}

info

The Blockchain.useConfiguration is a run-time alternative for statically defining contract addresses in the flow.json config file.

The configurations can be specified during the test setup as a best-practice.

e.g: Assume running a script that imports the above Foo.cdc contract. The import location for the contract can be specified using the placeholder "Foo". This placeholder can be any unique string.

Suppose this script is saved in say_hello.cdc.


_10
import "Foo"
_10
_10
access(all) fun main(): String {
_10
return Foo.sayHello()
_10
}

Then, before executing the script, the address mapping can be specified as follows:


_31
import Test
_31
_31
access(all) let blockchain = Test.newEmulatorBlockchain()
_31
access(all) let account = blockchain.createAccount()
_31
_31
access(all) fun setup() {
_31
blockchain.useConfiguration(Test.Configuration({
_31
"Foo": account.address
_31
}))
_31
_31
let contractCode = Test.readFile("Foo.cdc")
_31
let err = blockchain.deployContract(
_31
name: "Foo",
_31
code: contractCode,
_31
account: account,
_31
arguments: ["hello from args"],
_31
)
_31
_31
Test.expect(err, Test.beNil())
_31
}
_31
_31
access(all) fun testExample() {
_31
let script = Test.readFile("say_hello.cdc")
_31
let scriptResult = blockchain.executeScript(script, [])
_31
_31
Test.expect(scriptResult, Test.beSucceeded())
_31
_31
let returnValue = scriptResult.returnValue! as! String
_31
_31
Test.assertEqual("hello from args", returnValue)
_31
}

The subsequent operations on the blockchain (e.g: contract deployment, script/transaction execution) will resolve the import locations to the provided addresses.

Errors

An Error maybe returned when an operation (such as executing a script, executing a transaction, etc.) has failed. It contains a message indicating why the operation failed.


_10
// Error is returned if something has gone wrong.
_10
//
_10
access(all) struct Error {
_10
access(all) let message: String
_10
_10
init(_ message: String) {
_10
self.message = message
_10
}
_10
}

An Error can be asserted against its presence or absence.


_29
import Test
_29
_29
access(all) let blockchain = Test.newEmulatorBlockchain()
_29
access(all) let account = blockchain.createAccount()
_29
_29
access(all) fun testExample() {
_29
let script = Test.readFile("say_hello.cdc")
_29
let scriptResult = blockchain.executeScript(script, [])
_29
_29
// If we expect a script to fail, we can use Test.beFailed() instead
_29
Test.expect(scriptResult, Test.beSucceeded())
_29
_29
let tx = Test.Transaction(
_29
code: "transaction { prepare(acct: &Account) {} execute{} }",
_29
authorizers: [account.address],
_29
signers: [account],
_29
arguments: [],
_29
)
_29
let txResult = blockchain.executeTransaction(tx)
_29
_29
// If we expect a transaction to fail, we can use Test.beFailed() instead
_29
Test.expect(txResult, Test.beSucceeded())
_29
_29
let err: Test.Error? = txResult.error
_29
_29
if err != nil {
_29
log(err!.message)
_29
}
_29
}

Blockchain events

We can also assert that certain events were emitted from the blockchain, up to the latest block.

Suppose we have this contract (Foo.cdc):


_14
access(all) contract Foo {
_14
access(all) let msg: String
_14
_14
access(all) event ContractInitialized(msg: String)
_14
_14
init(_ msg: String) {
_14
self.msg = msg
_14
emit ContractInitialized(msg: self.msg)
_14
}
_14
_14
access(all) fun sayHello(): String {
_14
return self.msg
_14
}
_14
}


_31
import Test
_31
_31
access(all) let blockchain = Test.newEmulatorBlockchain()
_31
access(all) let account = blockchain.createAccount()
_31
_31
access(all) fun setup() {
_31
blockchain.useConfiguration(Test.Configuration({
_31
"Foo": account.address
_31
}))
_31
_31
let contractCode = Test.readFile("Foo.cdc")
_31
let err = blockchain.deployContract(
_31
name: "Foo",
_31
code: contractCode,
_31
account: account,
_31
arguments: ["hello from args"],
_31
)
_31
_31
Test.expect(err, Test.beNil())
_31
_31
// As of now, we have to construct the composite type by hand,
_31
// until the testing framework allows developers to import
_31
// contract types, e.g.:
_31
// let typ = Type<FooContract.ContractInitialized>()
_31
let typ = CompositeType("A.01cf0e2f2f715450.Foo.ContractInitialized")!
_31
let events = blockchain.eventsOfType(typ)
_31
Test.assertEqual(1, events.length)
_31
_31
// We can also fetch all events emitted from the blockchain
_31
log(blockchain.events())
_31
}

Commonly used contracts

The commonly used contracts are already deployed on the blockchain, and can be imported without any additional setup.

Suppose this script is saved in get_type_ids.cdc.


_16
import "FungibleToken"
_16
import "FlowToken"
_16
import "NonFungibleToken"
_16
import "MetadataViews"
_16
import "ViewResolver"
_16
import "ExampleNFT"
_16
import "NFTStorefrontV2"
_16
import "NFTStorefront"
_16
_16
access(all) fun main(): [String] {
_16
return [
_16
Type<FlowToken>().identifier,
_16
Type<NonFungibleToken>().identifier,
_16
Type<MetadataViews>().identifier
_16
]
_16
}


_20
import Test
_20
_20
access(all) let blockchain = Test.newEmulatorBlockchain()
_20
_20
access(all) fun testExample() {
_20
let script = Test.readFile("get_type_ids.cdc")
_20
let scriptResult = blockchain.executeScript(script, [])
_20
_20
Test.expect(scriptResult, Test.beSucceeded())
_20
_20
let returnValue = scriptResult.returnValue! as! [String]
_20
_20
let expected = [
_20
"A.0ae53cb6e3f42a79.FlowToken",
_20
"A.f8d6e0586b0a20c7.NonFungibleToken",
_20
"A.f8d6e0586b0a20c7.MetadataViews"
_20
]
_20
_20
Test.assertEqual(expected, returnValue)
_20
}

Reading from files

Writing tests often require constructing source-code of contracts/transactions/scripts in the test script. Testing framework provides a convenient way to load programs from a local file, without having to manually construct them within the test script.


_10
let contractCode = Test.readFile("./sample/contracts/FooContract.cdc")

readFile returns the content of the file as a string.

Logging

The log function is available for usage both in test scripts, as well as contracts/scripts/transactions.

The Blockchain.logs() method aggregates all logs from contracts/scripts/transactions.


_18
import Test
_18
_18
access(all) let blockchain = Test.newEmulatorBlockchain()
_18
access(all) let account = blockchain.createAccount()
_18
_18
access(all) fun testExample() {
_18
let tx = Test.Transaction(
_18
code: "transaction { prepare(acct: &Account) {} execute{ log(\"in a transaction\") } }",
_18
authorizers: [account.address],
_18
signers: [account],
_18
arguments: [],
_18
)
_18
_18
let txResult = blockchain.executeTransaction(tx)
_18
_18
Test.expect(txResult, Test.beSucceeded())
_18
Test.assertEqual(["in a transaction"], blockchain.logs())
_18
}

Examples

This repository contains some functional examples that demonstrate most of the above features, both for contrived and real-world smart contracts. It also contains a detailed explanation on using code coverage from within the testing framework.