Skip to main content
Sigil contracts are tested using the testlib crate, which provides a test runtime that simulates the blockchain environment. Tests use the same contract code that runs on-chain, with an in-memory backend for fast iteration.

Test Harness Setup

Test File Structure

The #[testlib::test] Attribute

Syntax:
Parameters:
  • mode - Optional: "regtest" for integration tests against real blockchain
What it provides:
  • runtime: &mut Runtime - Automatically injected variable
  • Test isolation (each test gets fresh state)
  • Automatic contract loading from compiled bytecode

Runtime API

Creating Test Identities

Each test can create multiple users:

Publishing Contracts

Contract addressing:
  • Contracts are identified by (name, height, tx_index)
  • In tests, first published contract is at height 1, tx_index 0
  • Second contract is at height 2, tx_index 0, etc.

Calling Contract Functions

Understanding the Double Question Mark

Generated contract functions return Result<Result<T, Error>>:
Why two Results?
  • Outer Result - Runtime errors (network, execution failures)
  • Inner Result - Contract errors (business logic failures)
Usage:

Testing View Functions

Testing Errors

Expected Business Logic Errors

Using is_err_and

Testing Cross-Contract Calls

Testing Re-entrancy Protection

The runtime prevents re-entrancy automatically:

Complete Test Example

Integration Tests (Regtest Mode)

Tests can run against a real Bitcoin regtest blockchain:
Regtest mode:
  • Runs against actual Bitcoin regtest
  • State persists across tests
  • Slower but provides full blockchain validation
  • Useful for final integration testing before testnet deployment

Running Tests

Test Organization

Test directory structure:
Test file (test/src/lib.rs):

Best Practices

1. Test both success and failure cases
2. Use descriptive test names
3. Factor out common setup

Quick Reference

Runtime API

The test runtime is automatically injected via the #[testlib::test] attribute: runtime.identity() -> Result<Signer>
  • Creates a new test identity (user) with automatic gas funding
runtime.publish(&signer, “name”) -> Result<ContractAddress>
  • Deploys a contract from compiled bytecode and returns its address
runtime.wit(&contract_address) -> Result<String>
  • Retrieves the WIT interface of a deployed contract

Test Attribute

Basic test:
Integration test (Bitcoin regtest):

Interface Macro

Generates type-safe bindings for contract calls:
Generated functions are async and take runtime, contract_address, and signer parameters:
Note: For tests, use interface! which generates bindings for any contract address. The import! macro (for fixed addresses) is rarely needed in tests.