Skip to main content

Add your description here

Project description

near-pytest

A pytest-native framework for testing NEAR smart contracts in Python.

PyPI version License: MIT Python Versions Built with Pytest

Features  •  📥 Installation  •  🚀 Getting Started  •  📊 Examples  •  📘 API Reference

Overview

near-pytest enables intuitive testing of NEAR smart contracts directly from Python. It provides a pytest-native approach that automatically handles compilation, sandbox initialization, account creation, contract deployment, and state management - allowing you to focus on writing tests that truly validate your contract's behavior.

Features

🚀 Zero-config setup - Everything works out-of-the-box: automatic sandbox management, contract compilation, and account creation

Lightning-fast tests - State snapshots between tests eliminate repeated setup operations, making test suites run orders of magnitude faster than traditional approaches

🧩 Intuitive API - Simple, Pythonic interfaces for interacting with contracts and accounts

🤝 Two testing styles - Choose between class-based tests or modular fixtures-based tests according to your preference

🔄 State snapshots - Create a full blockchain state once, then reset to this state between tests in milliseconds instead of seconds

🛠️ Complete toolchain integration - Seamless integration with NEAR compiler, Python SDK, and sandbox

🧪 Pytest native - Leverages the full power of pytest for smart contract testing

🔍 Rich logging - Detailed logs for troubleshooting and debugging

🧠 Smart caching - Automatically caches compiled contracts for faster subsequent runs

Requests-like Response API - Familiar interface for handling contract responses with .json() method and .text property

Installation

uv add near-pytest

Prerequisites

  • Python 3.11 or higher
  • For contract compilation: nearc package
  • The framework automatically handles downloading and installing the NEAR sandbox binary

Getting Started

You can choose between two testing approaches:

  1. Modular Fixtures Approach - More pytest-native, with composable fixtures
  2. Class-based Approach - Traditional approach with test classes inheriting from NearTestCase

Modular Fixtures Approach

import pytest
from near_pytest.compiler import compile_contract

@pytest.fixture(scope="session")
def counter_wasm():
    """Compile the counter contract."""
    return compile_contract("path/to/contract.py", single_file=True)

@pytest.fixture
def counter_contract(sandbox, counter_wasm):
    """Deploy a fresh counter contract for each test."""
    account = sandbox.create_random_account("counter")
    return sandbox.deploy(
        wasm_path=counter_wasm,
        account=account,
        init_args={"starting_count": 0}
    )

def test_increment(counter_contract, localnet_alice_account):
    """Test incrementing the counter."""
    # Call contract method using method chaining
    result = counter_contract.call("increment").as_transaction(localnet_alice_account)
    assert int(result) == 1
    
    # View state
    count = counter_contract.call("get_count").as_view()
    assert int(count) == 1

Class-based Approach

from near_pytest.testing import NearTestCase

class TestCounter(NearTestCase):
    @classmethod
    def setup_class(cls):
        # Call parent setup method first
        super().setup_class()
        
        # Compile the contract
        wasm_path = cls.compile_contract("path/to/contract.py", single_file=True)
        
        # Create account for contract
        cls.counter = cls.create_account("counter")
        
        # Deploy contract
        cls.counter_contract = cls.deploy_contract(
            cls.counter, 
            wasm_path, 
            init_args={"starting_count": 0}
        )
        
        # Create test accounts
        cls.alice = cls.create_account("alice")
        cls.bob = cls.create_account("bob")
        
        # Save initial state for future resets
        cls.save_state()

    def setup_method(self):
        # Reset to initial state before each test method
        self.reset_state()

    def test_increment(self):
        # Call contract method
        result = self.counter_contract.call("increment", {})
        assert int(result.text) == 1

Examples

Counter Contract Example (Fixtures Approach)

from near_pytest.modular import sandbox, compile_contract, sandbox_alice

@pytest.fixture(scope="session")
def counter_wasm():
    """Compile the counter contract."""
    return compile_contract("counter_contract/__init__.py", single_file=True)

@pytest.fixture
def fresh_counter(sandbox, counter_wasm, temp_account):
    """Deploy a fresh counter contract for each test."""
    return sandbox.deploy(
        wasm_path=counter_wasm,
        account=temp_account,
        init_args={"starting_count": 0}
    )

def test_increment_fresh(fresh_counter, sandbox_alice):
    """Test incrementing a fresh counter."""
    # This always starts with count=0
    result = fresh_counter.call("increment").as_transaction(sandbox_alice)
    assert int(result) == 1
    
    # Increment again
    result = fresh_counter.call("increment").as_transaction(sandbox_alice)
    assert int(result) == 2

JSON Response Example

def test_json_response(self):
    # Call a method that returns JSON data
    response = self.contract.call("get_user_data", {"user_id": "alice"})
    
    # Parse the JSON response
    data = response.json()
    
    # Assert on the parsed data
    assert data["name"] == "Alice"
    assert data["score"] == 100
    assert "created_at" in data

Key Concepts

1. Testing Styles

near-pytest supports two testing styles:

Modular Fixtures (Recommended)

  • More pytest-native approach with composable fixtures
  • Better test isolation with fixtures per test
  • Method chaining for contract calls with clear semantics
  • Easier to use with parallel testing (pytest-xdist)

Class-based (NearTestCase)

  • Traditional approach with a base class
  • All test methods share setup
  • State management with save_state and reset_state

2. Smart Contract Compilation

near-pytest automatically handles compilation of your Python smart contracts to WASM using the NEAR SDK for Python.

# Fixtures approach
wasm_path = compile_contract("path/to/contract.py", single_file=True)

# Class-based approach
wasm_path = cls.compile_contract("path/to/contract.py", single_file=True)

The compilation process includes:

  • Automatic caching of compiled contracts (based on content hash)
  • Support for single-file contracts or multi-file projects
  • Seamless integration with the nearc compiler

3. Sandbox Management

The framework automatically:

  • Downloads the appropriate NEAR sandbox binary for your platform
  • Manages sandbox lifecycle (start/stop)
  • Provides methods for state manipulation

4. Account Management

Create test accounts easily:

# Fixtures approach
account = sandbox.create_account("alice")
random_account = sandbox.create_random_account()

# Class-based approach
cls.alice = cls.create_account("alice")

5. Contract Deployment

Deploy contracts to accounts and initialize them:

# Fixtures approach
contract = sandbox.deploy(
    wasm_path=wasm_path,
    account=account,
    init_args={"param": "value"}
)

# Class-based approach
cls.contract = cls.deploy_contract(cls.account, wasm_path, init_args={"param": "value"})

6. Contract Calls

Call contract methods:

# Fixtures approach (with method chaining)
result = contract.call("increment").as_transaction(account)
view_result = contract.call("get_count").as_view()

# Class-based approach
result = contract.call("increment", {})
view_result = contract.view("get_count", {})

7. State Management

Save and restore state for fast test execution:

# Fixtures approach
state = sandbox.save_state()
sandbox.reset_state(state)

# Class-based approach
cls.save_state()
self.reset_state()

7. ContractResponse

Contract call responses are wrapped in a ContractResponse object that provides a familiar interface similar to Python's requests library:

# Get the raw text response
text_content = response.text

# Parse JSON response
json_data = response.json()

# String representation
str_value = str(response)  # Same as response.text

API Reference

Modular Fixtures

Provided Fixtures

  • sandbox: Main entry point for interacting with the NEAR sandbox
  • sandbox_alice, sandbox_bob: Pre-created accounts for tests
  • temp_account: Creates a fresh random account for each test
  • create_account: Factory fixture to create accounts on demand
  • near_client: Direct access to the NEAR client for RPC operations

SandboxProxy Methods

  • create_account(name): Create a new account with the given name
  • create_random_account(prefix="test"): Create a new account with a random name
  • deploy(wasm_path, account, init_args=None, init_method="new"): Deploy a contract
  • save_state(): Save current blockchain state
  • reset_state(state): Reset to a previously saved state

EnhancedContract Methods

  • call(method_name, **kwargs): Create a contract call with method chaining
  • account_id: Property to get the contract account ID

ContractCall Methods

  • as_transaction(account, amount=0, gas=None): Execute as a transaction
  • as_view(): Execute as a view call

Helper Functions

  • compile_contract(contract_path, single_file=False): Helper function to compile a contract to WASM

Using nearc Directly

If you prefer to use the NEAR compiler (nearc) directly for more control over the compilation process:

import nearc

# Compile your contract with custom options
wasm_path = nearc.builder.compile_contract(
    contract_path="path/to/contract.py", 
    output_path="path/to/output.wasm",
    single_file=True
)

Network Distinction

Our fixtures use a naming convention to distinguish between different networks:

  • sandbox - The main fixture that provides access to the NEAR sandbox node
  • localnet_alice, localnet_bob, localnet_temp_account - Account fixtures that operate on the local network

This distinction allows for future expansion to other networks like testnet or mainnet while maintaining a clear separation of concerns.

NearTestCase Methods

  • setup_class(cls): Set up shared resources for the test class
  • compile_contract(contract_path, single_file=False): Compile a contract to WASM
  • create_account(name, initial_balance=None): Create a new test account
  • deploy_contract(account, wasm_path, init_args=None): Deploy a contract
  • save_state(): Save the current state for later reset
  • reset_state(): Reset to the previously saved state

Account Methods

  • call_contract(contract_id, method_name, args=None, amount=0, gas=None): Call a contract method
  • view_contract(contract_id, method_name, args=None): Call a view method
  • deploy_contract(wasm_file): Deploy a contract to this account

Contract Methods

  • call(method_name, args=None, amount=0, gas=None): Call as the contract account
  • call_as(account, method_name, args=None, amount=0, gas=None): Call as another account
  • view(method_name, args=None): Call a view method

ContractResponse

A wrapper for contract call responses that provides a familiar interface for handling response data.

Properties

  • text: Get the raw text response as a string
  • transaction_result: Access the underlying transaction result (if available)

Methods

  • json(): Parse the response as JSON and return the resulting Python object

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

License

This project is licensed under the MIT License - see the LICENSE file for details.

Environment Variables

You can customize the behavior of near-pytest using these environment variables:

  • NEAR_PYTEST_LOG_LEVEL: Set logging level (DEBUG, INFO, WARNING, ERROR)
  • NEAR_SANDBOX_HOME: Specify a custom home directory for the sandbox

Architecture

near-pytest consists of several core components:

  • SandboxManager: Handles the NEAR sandbox process lifecycle
  • NearClient: Manages communication with the NEAR RPC interface
  • Account/Contract: Simplified models for interacting with the blockchain
  • ContractResponse: Wraps contract call responses with a user-friendly API
  • NearTestCase: Base class that ties everything together for testing

Troubleshooting

Common Issues

  1. Sandbox doesn't start

    • Check if port 3030 is available
    • Ensure you have proper permissions to execute downloaded binaries
  2. Contract compilation fails

    • Verify that the nearc package is installed
    • Check Python version compatibility (3.11+ required)
  3. Slow test execution

    • Ensure you're using save_state() and reset_state() pattern
    • Verify if cache directory (~/.near-pytest/cache) exists and is writeable

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

License

This project is licensed under the MIT License - see the LICENSE file for details.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

near_pytest-0.7.0.tar.gz (71.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

near_pytest-0.7.0-py3-none-any.whl (22.0 kB view details)

Uploaded Python 3

File details

Details for the file near_pytest-0.7.0.tar.gz.

File metadata

  • Download URL: near_pytest-0.7.0.tar.gz
  • Upload date:
  • Size: 71.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.6.8

File hashes

Hashes for near_pytest-0.7.0.tar.gz
Algorithm Hash digest
SHA256 3b519ffc247bab14edd165f5ce242586f9ba5453122c7c736034d700a47fdedb
MD5 6179f1c1a18c6e2f04739e3815945cc5
BLAKE2b-256 ce9a63550854ba53e9b91198b1980a53905461c5f2d7dc1d566163e79a4513bb

See more details on using hashes here.

File details

Details for the file near_pytest-0.7.0-py3-none-any.whl.

File metadata

File hashes

Hashes for near_pytest-0.7.0-py3-none-any.whl
Algorithm Hash digest
SHA256 18609cd40aa0fd509669b3d7d7397d7a33ee609186d507aee4da3558ffdb4b07
MD5 8cc4cac8d508348183f264aab9f7903c
BLAKE2b-256 6e43039cec00ba87d69afe808fc81af0b846796858a161bd7a502d77dd495947

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page