# How to Write Direct Mode Tests with the mock_web Fixture in the GenLayer Boilerplate

> Learn to write direct mode tests with the mock_web fixture in the GenLayer boilerplate. Intercept HTTP calls for deterministic contract testing with direct_vm.

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

---

**Direct-mode tests run contracts in-memory using the `direct_vm` harness, and the `mock_web` fixture lets you intercept external HTTP calls to return deterministic responses instead of performing real network requests.**

The `genlayer-project-boilerplate` repository ships with a lightweight test VM that executes GenLayer contracts locally. Writing direct mode tests with the `mock_web` fixture lets you validate contract logic that relies on `gl.nondet.web` without connecting to a live GenLayer Studio instance or the open internet.

## What Is Direct Mode Testing?

Direct mode is an in-memory execution environment for GenLayer contracts. The `direct_vm` object, provided by the boilerplate's test harness, deploys contracts locally and exposes helper methods to simulate callers, mock web traffic, and stub LLM prompts. This approach keeps tests fast and deterministic by removing external dependencies.

According to the `genlayer-project-boilerplate` source code, the direct-mode test suite lives under `tests/direct/` and is initialized through [`tests/direct/__init__.py`](https://github.com/genlayerlabs/genlayer-project-boilerplate/blob/main/tests/direct/__init__.py). The [`conftest.py`](https://github.com/genlayerlabs/genlayer-project-boilerplate/blob/main/conftest.py) file in that directory supplies shared helpers such as `to_hex` for address conversion.

## How the mock_web Fixture Works

The `mock_web` fixture is a method on the `direct_vm` instance. It registers a regular-expression pattern against a mocked HTTP response dictionary. When the contract invokes `gl.nondet.web.get`, `gl.nondet.web.post`, or `gl.nondet.web.render`, the VM compares the requested URL against registered patterns and returns the matching `status` and `body` instead of executing a real HTTP request.

This interception happens entirely inside the test harness. In [`contracts/football_bets.py`](https://github.com/genlayerlabs/genlayer-project-boilerplate/blob/main/contracts/football_bets.py), the `_check_match` method constructs a URL from the match date and calls `gl.nondet.web.render` to retrieve the score page before passing the result to an LLM prompt. The fixture is used extensively in [`tests/direct/test_views.py`](https://github.com/genlayerlabs/genlayer-project-boilerplate/blob/main/tests/direct/test_views.py) and [`tests/direct/test_resolve_bet.py`](https://github.com/genlayerlabs/genlayer-project-boilerplate/blob/main/tests/direct/test_resolve_bet.py), where it replaces the network layer for contracts that scrape web data before processing it with an LLM.

## Writing a Direct Mode Test with mock_web

A complete test that leverages `mock_web` follows a six-step workflow. The examples below reference the [`football_bets.py`](https://github.com/genlayerlabs/genlayer-project-boilerplate/blob/main/football_bets.py) contract, which fetches match results via `gl.nondet.web.render` and parses them with `gl.nondet.exec_prompt`.

### Deploy the Contract and Set the Caller

Use `direct_deploy` to load the contract file into memory, then assign `direct_vm.sender` so that `gl.message.sender_address` resolves to the correct account inside the contract.

```python
contract = direct_deploy("contracts/football_bets.py")
direct_vm.sender = direct_alice
alice = to_hex(direct_alice)

```

### Register URL Mocks with mock_web

Call `direct_vm.mock_web(pattern, response)` with a regex pattern that matches the URL your contract will request. The response must be a dictionary containing `status` and `body`.

```python
direct_vm.mock_web(
    r".*bbc\.com/sport/football/scores-fixtures.*",
    {"status": 200, "body": "Match result: 1:0. Winner: team 1."},
)

```

### Mock LLM Responses for Nondeterministic Pipelines

If the contract passes the web response to `gl.nondet.exec_prompt`, register a matching `mock_llm` pattern so the entire nondeterministic chain stays predictable.

```python
direct_vm.mock_llm(
    r".*Extract the match result.*",
    json.dumps({"score": "1:0", "winner": 1}),
)

```

### Invoke the Contract Method

Trigger the contract function that causes the external request. Because the URL matches the mocked pattern, the VM returns the stubbed response.

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

```

### Assert State Changes

Verify that the contract updated its internal state based on the mocked data.

```python
assert contract.get_player_points(alice) == 1
bet = contract.get_bets()[alice]["2024-06-20_spain_italy"]
assert bet.has_resolved is True
assert bet.real_winner == "1"
assert bet.real_score == "1:0"

```

## Complete Test Example

Here is a full test from the boilerplate that chains `mock_web` and `mock_llm` to resolve a winning bet.

```python
def test_resolve_winning_bet(direct_vm, direct_deploy, direct_alice):
    # 1️⃣ Deploy contract

    contract = direct_deploy("contracts/football_bets.py")

    # 2️⃣ Simulate caller

    direct_vm.sender = direct_alice
    alice = to_hex(direct_alice)

    # 3️⃣ Create a bet that will be resolved later

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

    # 4️⃣ Register web and LLM mocks for the match result

    direct_vm.mock_web(
        r".*bbc\.com/sport/football/scores-fixtures.*",
        {"status": 200, "body": "Match result: 1:0. Winner: team 1."},
    )
    direct_vm.mock_llm(
        r".*Extract the match result.*",
        json.dumps({"score": "1:0", "winner": 1}),
    )

    # 5️⃣ Resolve the bet – the contract will hit the mocked web endpoint

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

    # 6️⃣ Verify state

    assert contract.get_player_points(alice) == 1
    bet = contract.get_bets()[alice]["2024-06-20_spain_italy"]
    assert bet.has_resolved is True
    assert bet.real_winner == "1"
    assert bet.real_score == "1:0"

```

## Handling Multiple Mock Phases in One Test

When a single test function needs different responses for successive calls, invoke `direct_vm.clear_mocks()` between phases. This removes all registered `mock_web` and `mock_llm` stubs so you can register new ones without interference.

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

    # First match – win

    contract.create_bet("2024-06-20", "Spain", "Italy", "1")
    direct_vm.mock_web(r".*bbc.*", {"status": 200, "body": "ok"})
    direct_vm.mock_llm(r".*Extract.*", json.dumps({"score": "2:0", "winner": 1}))
    contract.resolve_bet("2024-06-20_spain_italy")
    assert contract.get_player_points(alice) == 1

    # Clear previous mocks to change the outcome

    direct_vm.clear_mocks()

    # Second match – loss

    contract.create_bet("2024-06-21", "France", "Germany", "2")
    direct_vm.mock_web(r".*bbc.*", {"status": 200, "body": "ok"})
    direct_vm.mock_llm(r".*Extract.*", json.dumps({"score": "0:1", "winner": 2}))
    contract.resolve_bet("2024-06-21_france_germany")
    assert contract.get_player_points(alice) == 1   # no additional point

```

## Key Files in the Boilerplate

The following files implement and demonstrate direct mode tests with `mock_web`:

- [`tests/direct/__init__.py`](https://github.com/genlayerlabs/genlayer-project-boilerplate/blob/main/tests/direct/__init__.py) – Initializes the direct-mode test environment.
- [`tests/direct/conftest.py`](https://github.com/genlayerlabs/genlayer-project-boilerplate/blob/main/tests/direct/conftest.py) – Provides helper utilities such as `to_hex` for address conversion.
- [`tests/direct/test_views.py`](https://github.com/genlayerlabs/genlayer-project-boilerplate/blob/main/tests/direct/test_views.py) – Contains basic view-method tests and introductory `mock_web` usage.
- [`tests/direct/test_resolve_bet.py`](https://github.com/genlayerlabs/genlayer-project-boilerplate/blob/main/tests/direct/test_resolve_bet.py) – Shows a full `mock_web` plus `mock_llm` workflow for bet resolution.
- [`contracts/football_bets.py`](https://github.com/genlayerlabs/genlayer-project-boilerplate/blob/main/contracts/football_bets.py) – The target contract that calls `gl.nondet.web.render` and `gl.nondet.exec_prompt`.

## Summary

- Direct mode executes contracts in-memory via the `direct_vm` harness, eliminating the need for a live GenLayer Studio instance.
- The `mock_web` fixture registers regex patterns that intercept `gl.nondet.web.get`, `gl.nondet.web.post`, and `gl.nondet.web.render` calls.
- Pair `mock_web` with `mock_llm` whenever the contract feeds web data into `gl.nondet.exec_prompt` to keep the entire pipeline deterministic.
- Use `direct_vm.sender` to set the caller address so `gl.message.sender_address` resolves correctly.
- Call `direct_vm.clear_mocks()` between test phases when you need to swap responses within the same function.
- Reference [`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_views.py`](https://github.com/genlayerlabs/genlayer-project-boilerplate/blob/main/tests/direct/test_views.py) for production-ready examples in the boilerplate.

## Frequently Asked Questions

### What HTTP methods does mock_web intercept?

The `mock_web` fixture intercepts calls made through `gl.nondet.web.get`, `gl.nondet.web.post`, and `gl.nondet.web.render`. When the contract code invokes any of these methods, the test VM matches the requested URL against registered regex patterns and returns the mocked `status` and `body` dictionary instead of performing a real network request.

### Do I need to mock the LLM separately from the web request?

Yes. If the contract logic passes the web response into `gl.nondet.exec_prompt`, you must also call `direct_vm.mock_llm` with a pattern that matches the prompt. This ensures the entire nondeterministic pipeline—from web fetch to structured extraction—returns predictable data under direct mode.

### How do I test multiple scenarios with different responses in one test?

Call `direct_vm.clear_mocks()` after the first scenario. This wipes all existing `mock_web` and `mock_llm` registrations, allowing you to define new patterns and responses for subsequent contract invocations without stale stubs interfering.

### What format should the mock_web response parameter use?

The response must be a Python dictionary with at least `status` and `body` keys, mimicking an HTTP response. For example: `{"status": 200, "body": "Match result: 1:0"}`. The VM returns exactly these values to the contract when the URL pattern matches.