GenLayer Test

GenLayer Testing Suite Reference

This document describes the key components and methods available in the GenLayer Test SDK for testing Intelligent Contracts.

For comprehensive test examples and implementation patterns, see our Test Suite Examples (opens in a new tab).

Installation

pip install genlayer-test

Configuration

The GenLayer Testing Suite can be configured using an optional gltest.config.yaml file in your project root. While not required, this file helps manage network configurations, contract paths, and environment settings.

Configuration File Structure

# gltest.config.yaml
networks:
  default: localnet  # Default network to use
 
  localnet:  # Local development network configuration (pre-configured)
    url: "http://127.0.0.1:4000/api"
    leader_only: false  # Run in leader-only mode by default
 
  studionet:  # Studio network configuration (pre-configured)
    # Pre-configured network - accounts are automatically generated
    # You can override any settings if needed
 
  testnet_asimov:  # Test network configuration (pre-configured)
    # Pre-configured network - requires accounts to be specified
    accounts:
      - "${ACCOUNT_PRIVATE_KEY_1}"
      - "${ACCOUNT_PRIVATE_KEY_2}"
      - "${ACCOUNT_PRIVATE_KEY_3}"
    from: "${ACCOUNT_PRIVATE_KEY_2}"  # Optional: specify default account
 
  custom_network:  # Custom network configuration
    id: 1234
    url: "http://custom.network:8545"
    accounts:
      - "${CUSTOM_ACCOUNT_1}"
      - "${CUSTOM_ACCOUNT_2}"
    from: "${CUSTOM_ACCOUNT_1}"  # Optional: specify default account
 
paths:
  contracts: "contracts"   # Path to your contracts directory
  artifacts: "artifacts"   # Path to your artifacts directory
 
environment: .env  # Path to your environment file containing private keys and other secrets

Configuration Sections

  1. Networks: Define different network environments

    • default: Specifies which network to use by default
    • Pre-configured networks:
      • localnet: Local development network with auto-generated test accounts
      • studionet: GenLayer Studio network with auto-generated test accounts
      • testnet_asimov: Public test network (requires account configuration)
    • Network configurations can include:
      • url: The RPC endpoint for the network (optional for pre-configured networks)
      • id: Chain ID (optional for pre-configured networks)
      • accounts: List of account private keys (using environment variables)
      • from: Which account to use by default for transactions (optional; defaults to first account)
      • leader_only: Whether to run deployments/writes only on the leader node
    • For custom networks (non-pre-configured), id, url, and accounts are required fields
  2. Paths: Define important directory paths

    • contracts: Location of your contract files
    • artifacts: Location to store analysis results and artifacts
  3. Environment: Path to your .env file containing sensitive information like private keys

If you don't provide a config file, the suite will use default values. You can override these settings using command-line arguments.

General

create_account

Creates a new account for testing purposes.

from gltest import create_account
 
account = create_account()

Parameters: None

Returns: A new account object

get_default_account

Returns the default account used to execute transactions when no account is specified.

from gltest import get_default_account
 
default_account = get_default_account()

Parameters: None

Returns: The default account object

get_accounts

Returns a collection of accounts available for testing. When a gltest.config.yaml file is present with account private keys defined for the current network, this function will return those loaded accounts. By design, the first account is the default account.

from gltest import get_accounts, get_default_account
 
accounts = get_accounts()
default_account = get_default_account()
assert default_account == accounts[0]
 
other_account = accounts[1]  # Get a different account
assert default_account != other_account

Parameters: None

Returns: A list of account objects loaded from the private keys defined in gltest.config.yaml for the current network, or pre-created test accounts if no config is present

Contract Interaction

For the following code examples, we'll use a Storage Intelligent Contract as a reference:

# { "Depends": "py-genlayer:test" }
 
from genlayer import *
 
class Storage(gl.Contract):
    # State variable to store data
    storage: str
 
    # Constructor - initializes the contract state
    def __init__(self, initial_storage: str):
        self.storage = initial_storage
 
    # Read method - marked with @gl.public.view decorator
    @gl.public.view
    def get_storage(self) -> str:
        return self.storage
 
    # Write method - marked with @gl.public.write decorator
    @gl.public.write
    def update_storage(self, new_storage: str) -> None:
        self.storage = new_storage

Contract Factory

The Contract Factory is used to deploy and build Contract objects that can interact with Intelligent Contract methods.

get_contract_factory

Retrieves a contract factory for a specific contract.

from gltest import get_contract_factory
 
def test_get_factory():
    factory = get_contract_factory('Storage')

Parameters:

  • contract_name: The name of the contract class to instantiate

Returns: A Contract Factory instance

deploy

Deploys a new contract instance and returns a Contract object.

from gltest import get_contract_factory, create_account
 
def test_deploy_contract():
    factory = get_contract_factory('Storage')
    
    custom_account = create_account()
    contract = factory.deploy(
        args=["initial_value"],  # Constructor arguments
        account=custom_account,  # Account to deploy from
        consensus_max_rotations=3,  # Optional: max consensus rotations
    )

Parameters:

  • args: Contract constructor arguments
  • account: (Optional) Account to use for deployment
  • consensus_max_rotations: (Optional) Maximum number of consensus rotations
  • wait_interval: (Optional) Wait interval in milliseconds for transaction status
  • wait_retries: (Optional) Number of retries for transaction status
  • wait_transaction_status: (Optional) Desired transaction status

Returns: A Contract object

build_contract

Builds a Contract object from an existing deployed contract.

from gltest import get_contract_factory, create_account
 
def test_build_contract():
    factory = get_contract_factory('Storage')
    
    custom_account = create_account()
    contract = factory.build_contract(
        contract_address="0xabcd...z",
        account=custom_account  # Optional: use this for subsequent transaction calls
    )

Parameters:

  • contract_address: The address of the deployed contract
  • account: (Optional) Account to use for contract interactions

Returns: A Contract object

Contract Methods

read methods

Calls read-only methods on the contract.

from gltest import get_contract_factory
 
def test_read_methods():
    factory = get_contract_factory('Storage')
    contract = factory.deploy(args=["initial_value"])
 
    # Call a read-only method
    result = contract.get_storage(args=[]).call()
 
    assert result == "initial_value"

Parameters:

  • args: Method arguments

Returns: The result of the contract read call

write methods

Calls state-modifying methods on the contract.

from gltest import get_contract_factory
from gltest.assertions import tx_execution_succeeded
 
def test_write_methods():
    factory = get_contract_factory("Storage")
    contract = factory.deploy(args=["initial_value"])
    
    # Call a write method with arguments
    tx_receipt = contract.update_storage(
        args=["new_value"],  # Method arguments
    ).transact(
        value=0,  # Optional amount of native token to send
        consensus_max_rotations=3,
        wait_interval=1,
        wait_retries=10,
    )
    
    # Verify the transaction was successful
    assert tx_execution_succeeded(tx_receipt)
    
    # Verify the value was updated
    assert contract.get_storage().call() == "new_value"

Parameters:

  • args: Method arguments
  • value: (Optional) Amount of native token to send with the transaction
  • consensus_max_rotations: (Optional) Maximum number of consensus rotations
  • wait_interval: (Optional) Wait interval in milliseconds for transaction status
  • wait_retries: (Optional) Number of retries for transaction status
  • wait_transaction_status: (Optional) Desired transaction status
  • wait_triggered_transactions: (Optional) Whether to wait for triggered transactions
  • wait_triggered_transactions_status: (Optional) Desired triggered transaction status

Returns: The transaction receipt

connect

Creates a new contract instance that uses a different account for transactions.

from gltest import create_account
 
other_account = create_account()
contract_with_other_account = contract.connect(other_account)

Parameters:

  • account: Account to use for contract interactions

Returns: A new Contract instance using the specified account

Helpers

load_fixture

Runs a fixture function and returns its value at the same state in every test. load_fixture sets up the state on its first call and returns to that state in subsequent tests. This is particularly useful for setting up test environments that need to be in a consistent state across multiple tests.

Environment Behavior:

  • Local Studio: The fixture's state is preserved between test runs, allowing for consistent test environments.
  • Hosted Studio: When using https://studio.genlayer.com/api, the fixture executes independently for each test run without state preservation.
from gltest import get_contract_factory
from gltest.helpers import load_fixture
from gltest.assertions import tx_execution_succeeded
 
# Define a fixture that deploys a contract
def deploy_contract():
    factory = get_contract_factory("Storage")
    contract = factory.deploy(args=["initial_value"])
    return contract
 
# Test A: Verify initial state
def test_initial_state():
    # Load the fixture - will deploy contract on first run
    storage_contract = load_fixture(deploy_contract)
    
    # Verify initial state
    current_storage = storage_contract.get_storage(args=[]).call()
    assert current_storage == "initial_value"
 
# Test B: Verify state persistence and updates
def test_state_updates():
    # Load the same fixture - will reuse deployed contract
    storage_contract = load_fixture(deploy_contract)
 
    # Verify initial state is preserved
    current_storage = storage_contract.get_storage(args=[]).call()
    assert current_storage == "initial_value"
 
    # Update the storage
    tx_receipt = storage_contract.update_storage(
        args=["new_value"]
    ).transact()
 
    # Verify the update was successful
    assert tx_execution_succeeded(tx_receipt)
    assert storage_contract.get_storage().call() == "new_value"

Fixtures

The GenLayer Testing Suite provides reusable pytest fixtures in gltest.fixtures to simplify common testing operations. These fixtures can be imported and used in your test files to avoid repetitive setup code.

Available Fixtures

  • gl_client (session scope): GenLayer client instance for network operations
  • default_account (session scope): Default account for testing and deployments
  • accounts (session scope): List of test accounts for multi-account scenarios
  • setup_validators (function scope): Function to create test validators for LLM operations

gl_client

Provides a GenLayer PY client instance that's created once per test session.

def test_client_operations(gl_client):
    tx_hash = "0x1234..."
    transaction = gl_client.get_transaction(tx_hash)

default_account

Default account used when no account is specified.

def test_with_default_account(default_account):
    factory = get_contract_factory("MyContract")
    contract = factory.deploy(account=default_account)

accounts

List of account objects loaded from the current network config or pre-created test accounts.

def test_multiple_accounts(accounts):
    sender = accounts[0]
    receiver = accounts[1]
    tx_receipt = contract.transfer(args=[receiver.address, 100]).transact()

setup_validators

Creates test validators for localnet environment. Useful for testing LLM-based contract methods and consensus behavior.

def test_with_validators(setup_validators):
    # Default configuration
    setup_validators()
    
    # With custom mock responses and validator count
    mock_response = {"result": "mocked LLM response"}
    setup_validators(mock_response=mock_response, n_validators=3)
    
    contract = factory.deploy()
    result = contract.llm_based_method()

Parameters for setup_validators:

  • mock_response (dict, optional): Mock validator response when using --test-with-mocks
  • n_validators (int, optional): Number of validators to create (default: 5)

Assertions

Assertions are utility functions to verify the outcome of contract transactions in your tests.

tx_execution_succeeded

Asserts that a transaction executed successfully.

from gltest.assertions import tx_execution_succeeded
 
assert tx_execution_succeeded(tx_receipt)

You can also match specific patterns in the transaction's stdout output:

# Simple string matching
assert tx_execution_succeeded(tx_receipt, match_std_out="Process completed")
 
# Regex pattern matching
assert tx_execution_succeeded(tx_receipt, match_std_out=r".*code \d+")

Statistical Analysis with .analyze()

The .analyze() method performs statistical analysis for contract methods over multiple runs. This is particularly useful for testing LLM-based contracts where outputs may vary.

from gltest import get_contract_factory
 
def test_analyze_method():
    factory = get_contract_factory("LlmContract")
    contract = factory.deploy()
    
    analysis = contract.process_with_llm(args=["input_data"]).analyze(
        provider="openai",
        model="gpt-4o",
        runs=100,
        config=None,
        plugin=None,
        plugin_config=None,
    )
    
    print(f"Method: {analysis.method}")
    print(f"Success rate: {analysis.success_rate:.2f}%")
    print(f"Reliability score: {analysis.reliability_score:.2f}%")
    print(f"Unique states: {analysis.unique_states}")
    print(f"Execution time: {analysis.execution_time:.1f}s")

The analysis returns a summary object with:

  • method, args
  • total_runs, successful_runs, failed_runs
  • unique_states, reliability_score
  • execution_time

tx_execution_failed

Asserts that a transaction execution failed.

from gltest.assertions import tx_execution_failed
 
assert tx_execution_failed(tx_receipt)

Mock LLM Responses

The Mock LLM system allows you to simulate Large Language Model responses in GenLayer tests. This enables deterministic tests by providing predefined responses instead of relying on actual LLM calls.

Basic Structure

mock_response = {
    "response": {},                               # Optional: mocks gl.nondet.exec_prompt
    "eq_principle_prompt_comparative": {},        # Optional: mocks gl.eq_principle.prompt_comparative
    "eq_principle_prompt_non_comparative": {}     # Optional: mocks gl.eq_principle.prompt_non_comparative
}
 
setup_validators(mock_response)

Method Mappings

  • "response" -> gl.nondet.exec_prompt
  • "eq_principle_prompt_comparative" -> gl.eq_principle.prompt_comparative
  • "eq_principle_prompt_non_comparative" -> gl.eq_principle.prompt_non_comparative

How It Works

The mock system performs substring matching on the internally constructed user message. If a key in your mock dictionary is contained within the actual user message, the associated response is returned.

Substring Matching Examples

Will work (partial match):

"eq_principle_prompt_comparative": {
    "The value of give_coin": True
}

Won't work (extra words break the match):

"eq_principle_prompt_comparative": {
    "The good value of give_coin": True
}

Complete Example

from gltest import get_contract_factory
 
def test_with_mocked_llm(setup_validators):
    mock_response = {
        "response": {
            "What is the weather?": "It's sunny today",
            "Calculate 2+2": "4"
        },
        "eq_principle_prompt_comparative": {
            "values must be equal": True,
            "amounts should match": False
        },
        "eq_principle_prompt_non_comparative": {
            "Is this valid?": True
        }
    }
    
    setup_validators(mock_response)
    
    factory = get_contract_factory("MyLLMContract")
    contract = factory.deploy()
    result = contract.check_weather()

Notes:

  • Mock responses are available when running tests on localnet
  • Use with the --test-with-mocks flag to enable mocking

You can also match specific patterns in the transaction's stderr output:

# Simple string matching
assert tx_execution_failed(tx_receipt, match_std_err="Warning: deprecated")
 
# Regex pattern matching
assert tx_execution_failed(tx_receipt, match_std_err=r"Method.*failed")

Parameters:

  • transaction_receipt: The transaction receipt object to check
  • match_std_out (optional): String or regex pattern to match in stdout
  • match_std_err (optional): String or regex pattern to match in stderr

Returns: True if the transaction failed and patterns match (if provided), otherwise False

Note: The stdout/stderr matching feature is only available when running on studionet and localnet. These features are not supported on testnet.

Running Tests

The GenLayer Test SDK provides a command-line interface for executing tests with various configurations.

Basic Usage

Run all tests in the current directory:

gltest

Run a specific test file:

gltest tests/test_mycontract.py

Test Execution Control

Filter tests by markers:

gltest -m "integration"  # Run only tests marked as integration

Control test output verbosity:

gltest -v              # Enable verbose output
gltest -vv             # Enable more verbose output

Configuration Options

Specify a custom contracts directory (default: contracts/):

gltest --contracts-dir <path_to_contracts>

Specify artifacts directory (default: artifacts/):

gltest --artifacts-dir <path_to_artifacts>

Select a network configuration from your gltest.config.yaml:

# Run tests on localnet (default)
gltest --network localnet
 
# Run tests on Studio
gltest --network studionet
 
# Run tests on testnet
gltest --network testnet_asimov

Set a custom RPC endpoint:

gltest --rpc-url <custom_rpc_url>

Configure transaction receipt polling:

# Set the polling interval in milliseconds
gltest --default-wait-interval <milliseconds>
 
# Set the maximum number of polling attempts
gltest --default-wait-retries <number_of_retries>

Run tests with mocked LLM responses (localnet only):

gltest --test-with-mocks

Run tests with leader-only mode enabled (studio-based networks only: localhost, 127.0.0.1, *.genlayer.com, *.genlayerlabs.com):

gltest --leader-only

When enabled, deployments and write operations run only on the leader node. On unsupported networks, this flag has no effect and a warning is logged.

Note: All configuration options can be combined. For example:

gltest -v --network testnet_asimov --default-wait-interval 1000