# How to Test Contract Reverts in GenLayer Direct Mode

> Easily test contract reverts in GenLayer direct mode using pytest expect revert. Assert smart contract calls raise specific errors without live deployment.

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

---

**Use the `direct_vm.expect_revert()` context manager in your pytest functions to assert that smart contract calls raise specific error messages without deploying to a live network.**

GenLayer's direct-mode testing framework allows developers to validate smart contract behavior locally by simulating the GenVM environment in-memory. This article demonstrates how to use the `direct_vm` fixture and its `expect_revert` method to test failure conditions in the `genlayerlabs/genlayer-project-boilerplate` repository. Mastering this pattern ensures your contracts handle edge cases correctly before deployment.

## Understanding the `direct_vm` Fixture

The **`direct_vm`** fixture is provided by the GenLayer SDK's direct-mode plugin, configured in [`tests/direct/conftest.py`](https://github.com/genlayerlabs/genlayer-project-boilerplate/blob/main/tests/direct/conftest.py). It instantiates a mock GenVM environment that loads your contract bytecode and exposes utilities for transaction simulation, including **`mock_web`**, **`mock_llm`**, **`clear_mocks`**, and the **`expect_revert`** context manager.

This fixture eliminates the need for a live network or Studio instance by running contracts directly against a Python-based VM implementation. You can set the transaction sender using `direct_vm.sender = <account>` and configure deterministic responses for external web calls and LLM invocations before asserting revert conditions.

### How `expect_revert` Intercepts Exceptions

The `expect_revert` method creates a context manager that catches **`gl.vm.UserError`** exceptions (or standard Python `Exception` types) raised by contract code. When invoked with an error message substring, it verifies that the contract raises a matching error within the context block.

If the contract executes without reverting, or if the error message does not contain the expected substring, the test fails immediately. This behavior is implemented in the GenLayer testing utilities attached to the `DirectVM` class provided by the SDK.

## Testing Common Revert Scenarios

Developers typically verify three categories of revert conditions: duplicate state entries, authorization failures, and unfinished external dependencies. Each scenario follows the same pattern: configure the contract state, optionally mock external responses, then wrap the failing call in `with direct_vm.expect_revert("message"):`.

### Preventing Duplicate State Entries

In [`tests/direct/test_create_bet.py`](https://github.com/genlayerlabs/genlayer-project-boilerplate/blob/main/tests/direct/test_create_bet.py), tests verify that creating a bet with an existing key reverts to prevent duplicate entries. The contract stores bets in a mapping keyed by match identifiers, and attempts to overwrite existing entries trigger a revert.

```python
def test_create_duplicate_bet_fails(direct_vm, direct_deploy, direct_alice):
    contract = direct_deploy("contracts/football_bets.py")
    direct_vm.sender = direct_alice

    # First bet creation succeeds

    contract.create_bet("2024-06-20", "Spain", "Italy", "1")

    # Attempting to create the same bet again should revert

    with direct_vm.expect_revert("Bet already created"):
        contract.create_bet("2024-06-20", "Spain", "Italy", "1")

```

### Enforcing State Machine Rules

The file [`tests/direct/test_resolve_bet.py`](https://github.com/genlayerlabs/genlayer-project-boilerplate/blob/main/tests/direct/test_resolve_bet.py) demonstrates testing business logic constraints, such as preventing resolution of already-resolved bets or games that haven't finished. These tests verify that the contract maintains internal state integrity across multiple transactions.

```python
def test_resolve_already_resolved_fails(direct_vm, direct_deploy, direct_alice):
    contract = direct_deploy("contracts/football_bets.py")
    direct_vm.sender = direct_alice

    contract.create_bet("2024-06-20", "Spain", "Italy", "1")
    
    # Mock successful match completion

    direct_vm.mock_web(r".*bbc\.com.*", {"status": 200, "body": "Result"})
    direct_vm.mock_llm(r".*Extract.*", '{"score": "1:0", "winner": 1}')

    # First resolution succeeds

    contract.resolve_bet("2024-06-20_spain_italy")

    # Second resolution must revert

    with direct_vm.expect_revert("Bet already resolved"):
        contract.resolve_bet("2024-06-20_spain_italy")

```

### Validating External Dependencies

When contract logic depends on web scrapes or LLM extractions, you must mock these responses to trigger specific revert paths. For example, verifying that resolving an unfinished game reverts requires simulating a pending match status.

```python
def test_resolve_unfinished_game_fails(direct_vm, direct_deploy, direct_alice):
    contract = direct_deploy("contracts/football_bets.py")
    direct_vm.sender = direct_alice

    contract.create_bet("2024-06-20", "Spain", "Italy", "1")
    
    # Mock incomplete match data

    direct_vm.mock_web(r".*bbc\.com.*", {"status": 200, "body": "Pending"})
    direct_vm.mock_llm(r".*Extract.*", '{"score": "-", "winner": -1}')

    with direct_vm.expect_revert("Game not finished"):
        contract.resolve_bet("2024-06-20_spain_italy")

```

## Complete Test Implementation Pattern

Combine state setup, mocking, and revert assertion into a comprehensive test workflow. Always deploy the contract using `direct_deploy`, set the sender address, configure any necessary mocks, execute the successful state transitions, then assert the revert condition.

```python
import json

def test_unauthorized_resolution_reverts(direct_vm, direct_deploy, direct_alice, direct_bob):
    # Deploy and setup

    contract = direct_deploy("contracts/football_bets.py")
    direct_vm.sender = direct_alice
    contract.create_bet("2024-06-20", "Spain", "Italy", "1")
    
    # Switch to unauthorized user

    direct_vm.sender = direct_bob
    
    # Attempt unauthorized action

    with direct_vm.expect_revert("Not authorized"):
        contract.cancel_bet("2024-06-20_spain_italy")

```

## Summary

- The **`direct_vm`** fixture provides an in-memory GenVM simulation via [`tests/direct/conftest.py`](https://github.com/genlayerlabs/genlayer-project-boilerplate/blob/main/tests/direct/conftest.py), enabling local contract testing without network deployment.
- Use **`direct_vm.expect_revert("substring")`** as a context manager to assert that contract calls raise errors containing specific message text.
- The context manager catches **`gl.vm.UserError`** and standard Python exceptions raised within the contract code executed in direct mode.
- Configure external dependencies using **`direct_vm.mock_web()`** and **`direct_vm.mock_llm()`** before testing reverts that depend on oracle or web scrape data.
- Test files such as [`tests/direct/test_resolve_bet.py`](https://github.com/genlayerlabs/genlayer-project-boilerplate/blob/main/tests/direct/test_resolve_bet.py) and [`tests/direct/test_create_bet.py`](https://github.com/genlayerlabs/genlayer-project-boilerplate/blob/main/tests/direct/test_create_bet.py) demonstrate production patterns for validating state machine integrity and duplicate prevention.

## Frequently Asked Questions

### What exception type does `expect_revert` catch?

The `expect_revert` context manager catches **`gl.vm.UserError`** exceptions raised by the GenLayer VM, along with standard Python `Exception` types. When the contract code executes `raise gl.vm.UserError("message")` or raises a standard exception, the context manager compares the error message against the expected substring provided in the method call.

### Can I use regular expressions with `expect_revert`?

Based on the current implementation in `genlayerlabs/genlayer-project-boilerplate`, `expect_revert` performs substring matching rather than regex pattern matching. Supply the exact error message substring or unique identifier text that appears within the contract's revert message to ensure reliable test assertions.

### Where is the `direct_vm` fixture defined?

The **`direct_vm`** fixture is defined in [`tests/direct/conftest.py`](https://github.com/genlayerlabs/genlayer-project-boilerplate/blob/main/tests/direct/conftest.py) and is provided by the GenLayer SDK's direct-mode testing plugin. This fixture instantiates a `DirectVM` class that mocks the complete GenLayer execution environment, including transaction context, external web calls, and LLM integrations.

### Do I need a live network to test reverts in direct mode?

No. Direct mode operates entirely in-memory using the mock VM provided by the `direct_vm` fixture. This allows you to test contract reverts, state transitions, and external dependency integrations locally without connecting to GenLayer Studio or deploying to a testnet, significantly accelerating the development feedback loop.