# How to Create Custom Test Fixtures in GenLayer Direct Mode: A Complete Guide

> Learn to create custom test fixtures in GenLayer direct mode by composing core fixtures in conftest.py. Streamline your testing with reusable utilities and decorators.

- Repository: [GenLayer Labs/genlayer-project-boilerplate](https://github.com/genlayerlabs/genlayer-project-boilerplate)
- Tags: how-to-guide
- Published: 2026-08-20

---

**Define custom pytest fixtures in [`tests/direct/conftest.py`](https://github.com/genlayerlabs/genlayer-project-boilerplate/blob/main/tests/direct/conftest.py) by importing core fixtures like `direct_vm`, `direct_deploy`, and `direct_alice`, then compose them into reusable test utilities using `@pytest.fixture` decorators.**

GenLayer's **direct mode** provides an in-memory virtual machine with the full GenVM stack, enabling fast unit-style tests without deploying to a live Studio environment. The test runner ships with essential fixtures that expose the virtual machine, deployment helpers, and pre-configured accounts. When you need reusable test logic—such as mock web responses, pre-deployed contract instances, or address conversion utilities—you create **custom fixtures** that compose these built-in primitives.

## Understanding Direct Mode Fixtures

Direct mode testing relies on pytest fixtures provided by the `genlayer-dev` plugin. These core fixtures are automatically available in any test file under `tests/direct/`:

| Fixture | Purpose | Key Methods/Attributes |
|---------|---------|------------------------|
| `direct_vm` | **VirtualMachine** instance running the GenVM stack | `mock_web()`, `mock_llm()`, `clear_mocks()` |
| `direct_deploy` | Helper to deploy contracts from file paths | Returns deployed contract object callable |
| `direct_alice`, `direct_bob`, etc. | Deterministic **Address** objects for test accounts | `.as_hex` property for checksummed hex format |

You never implement these fixtures yourself—they're registered automatically when the test suite runs.

## Creating Custom Fixtures in conftest.py

Pytest discovers custom fixtures from [`tests/direct/conftest.py`](https://github.com/genlayerlabs/genlayer-project-boilerplate/blob/main/tests/direct/conftest.py) and makes them available to all direct-mode test files. The standard pattern involves importing core fixtures as arguments and returning composed objects.

### Basic Fixture Structure

```python

# tests/direct/conftest.py

import pytest

@pytest.fixture
def my_custom_fixture(direct_vm, direct_deploy):
    # Compose built-in fixtures into reusable test utility

    contract = direct_deploy("contracts/my_contract.py")
    direct_vm.mock_web(r".*example\.com.*", {"status": 200, "body": "{}"})
    return contract

```

### Address Conversion Helper

The repository includes a practical utility for converting address bytes to checksummed hex format. In [`tests/direct/conftest.py`](https://github.com/genlayerlabs/genlayer-project-boilerplate/blob/main/tests/direct/conftest.py), lines 4-15 define:

```python

# tests/direct/conftest.py

def to_hex(addr_bytes):
    """Convert address bytes to checksummed hex matching contract output."""
    if hasattr(addr_bytes, "as_hex"):
        return addr_bytes.as_hex
    from genlayer.py.types import Address
    return Address(addr_bytes).as_hex

```

Convert this to an auto-injectable fixture:

```python

# tests/direct/conftest.py

import pytest

@pytest.fixture
def hex_address(direct_alice):
    """Yield Alice's address as a checksummed hex string."""
    from genlayer.py.types import Address
    return Address(direct_alice).as_hex

```

Now tests simply declare `hex_address` as a parameter:

```python
def test_player_points_default_zero(direct_deploy, hex_address):
    contract = direct_deploy("contracts/football_bets.py")
    assert contract.get_player_points(hex_address) == 0

```

## Common Custom Fixture Patterns

### Reusable Contract Instance

When multiple tests interact with the same contract, avoid repetitive deployment:

```python
@pytest.fixture
def football_contract(direct_deploy):
    """Pre-deployed football bets contract for reuse across tests."""
    return direct_deploy("contracts/football_bets.py")

```

### Mock Web and LLM Responses

Direct mode excels at testing contracts with intelligent contract calls. Create fixtures that pre-configure expected responses:

```python
@pytest.fixture
def mock_successful_match(direct_vm):
    """
    Configure VM to return successful match data for BBC sports queries
    and structured extraction from LLM responses.
    """
    # Mock HTTP web query

    direct_vm.mock_web(
        r".*bbc\.com.*",
        {"status": 200, "body": "Match results: Team A 1 - 0 Team B"}
    )
    # Mock LLM extraction

    direct_vm.mock_llm(
        r".*Extract.*",
        '{"score": "1:0", "winner": 1}'
    )
    yield  # Provide control to test

    direct_vm.clear_mocks()  # Cleanup after test completes

```

### Parameterized Test Data

Use `params` to run the same test against multiple scenarios:

```python
@pytest.fixture(params=[
    {"home": "Arsenal", "away": "Chelsea", "expected_winner": 0},
    {"home": "Liverpool", "away": "Man City", "expected_winner": 1},
])
def bet_scenario(request):
    return request.param

```

## Fixture Scoping for Performance

Control fixture lifecycle with the `scope` parameter:

| Scope | When Created | Best For |
|-------|--------------|----------|
| `"function"` (default) | Every test | Isolated state, mocks that change per test |
| `"module"` | Once per test file | Expensive contract compilation |
| `"session"` | Once for entire run | Immutable configuration, shared resources |

Example module-scoped fixture:

```python
@pytest.fixture(scope="module")
def compiled_football_contract(direct_deploy):
    """Deploy once and reuse—safe if contract state is reset between tests."""
    return direct_deploy("contracts/football_bets.py")

```

## Real-World Examples from the Repository

### test_views.py: Basic Fixture Usage

The file [`tests/direct/test_views.py`](https://github.com/genlayerlabs/genlayer-project-boilerplate/blob/main/tests/direct/test_views.py) demonstrates fundamental patterns using `direct_deploy`, `direct_vm`, and `direct_alice` to test contract view functions without side effects.

### test_create_bet.py and test_resolve_bet.py

[`tests/direct/test_create_bet.py`](https://github.com/genlayerlabs/genlayer-project-boilerplate/blob/main/tests/direct/test_create_bet.py) and [`tests/direct/test_resolve_bet.py`](https://github.com/genlayerlabs/genlayer-project-boilerplate/blob/main/tests/direct/test_resolve_bet.py) show advanced fixture composition:

- Mocking web queries for real-time sports data
- Mocking LLM responses for natural language extraction
- Chaining multiple fixtures for complex bet resolution flows

## Summary

- **Place custom fixtures** in [`tests/direct/conftest.py`](https://github.com/genlayerlabs/genlayer-project-boilerplate/blob/main/tests/direct/conftest.py) for automatic discovery across the direct-mode test suite
- **Import core fixtures** (`direct_vm`, `direct_deploy`, `direct_alice`, etc.) as function arguments to compose your own
- **Return values** that tests need repeatedly—contract instances, formatted addresses, or pre-configured mock states
- **Use appropriate scoping** (`function`, `module`, `session`) to balance isolation with performance
- **Clean up resources** using `yield` fixtures when mocks or state need resetting

## Frequently Asked Questions

### What's the difference between direct mode and Studio deployment testing?

Direct mode runs contracts in-memory using the full GenVM stack without network calls, making tests execute in milliseconds. Studio deployment testing requires a running GenLayer node and validates actual consensus behavior. Use direct mode for rapid iteration on contract logic; use Studio testing for integration validation before production deployment.

### Can I use custom fixtures from conftest.py in regular (non-direct) tests?

Fixtures in [`tests/direct/conftest.py`](https://github.com/genlayerlabs/genlayer-project-boilerplate/blob/main/tests/direct/conftest.py) are isolated to that directory. If you need fixtures for both direct and Studio tests, define them in a parent [`tests/conftest.py`](https://github.com/genlayerlabs/genlayer-project-boilerplate/blob/main/tests/conftest.py) or import explicitly. The core `direct_*` fixtures are only meaningful in direct mode since they require the in-memory VM.

### How do I debug a fixture that's not being discovered?

Verify the file is named exactly [`conftest.py`](https://github.com/genlayerlabs/genlayer-project-boilerplate/blob/main/conftest.py) (not [`conf_test.py`](https://github.com/genlayerlabs/genlayer-project-boilerplate/blob/main/conf_test.py)), resides in `tests/direct/`, and contains valid Python with no syntax errors. Run `pytest --fixtures tests/direct/` to list all available fixtures—your custom fixture should appear if discovery is working.

### Why does my mock persist across tests when I expected cleanup?

Always use `yield` with a cleanup section, or call `direct_vm.clear_mocks()` explicitly. Alternatively, rely on `scope="function"` (the default) which recreates the fixture—and its mocks—fresh for each test. Module-scoped fixtures with mutable mock state require manual cleanup to prevent test pollution.