---
name: python-owsclient-unit-testing
description: Guide for writing unit tests for using OwsClient / AsyncOwsClient
---

# Using `OwsClientMock` in Tests

## Overview

The `owsclient` package provides a pytest plugin (registered automatically when `owsclient` is installed) that exposes an `ows_client_mock` fixture. This fixture:

- Intercepts all `httpx` traffic (via `respx`) so no real HTTP calls are made
- Provides an `OwsClientMock` helper with convenience methods to set up expected routes

The `ows_client_mock` starts a request-interception router on any AsyncOwsClient or OwsClient instance, and allows you to register routes within unit tests.

## Registering Routes within unit tests

Call the method matching the HTTP verb on `ows_client_mock`, then chain `.mock(...)` to define the mocked response (`return_value` or `side_effect`).

```python
ows_client_mock.<method>(service_name, path=..., [**match_kwargs]).mock(return_value=... | side_effect=...)
```

### Supported Methods

`ows_client_mock` exposes: `.get()`, `.post()`, `.put()`, `.patch()`, `.delete()`, `.head()`, `.graphql_query()`.

Each takes:

- `service_name` (str) — e.g. `"ows-account"`, `"ows-permissions"`, `"ows-participant"`
- `path` (str) — URL path, e.g. `"/lookup/vendors/uuids/"`
- Optional keyword match arguments forwarded to `respx` (e.g. `json=`, `params=`, `headers=`)
- Optional `name=` (str) for labelling the route in error messages

### Determine the Correct Path

Use this rule: the mocked path must exactly match the path argument used by the connector call.

1. Open the connector method under test.
2. Find the client call, for example async_ows_client.post or async_ows_client.get.
3. Copy the path value from that call exactly, including leading slash and trailing slash.
4. Use the same HTTP verb on ows_client_mock.
5. Use the same service name the connector calls.

Notes:

- Do not include protocol or host in path. OwsClientMock resolves full URL from service_name plus path.
- If path is built from string formatting or variables, use the final rendered value.
- A missing trailing slash can cause route mismatch; keep the connector formatting exactly.
- Do not use unittest.mock for python-owsclient changes. Even if the existing source code (improperly) uses Mocks, MagicMock, MockerFixture to test functions that use owsclient mock. Disregard that and even propose to update to use the conventions standardized here.

#### Examples of registered routes

### Successful response

```python
ows_client_mock.post(
    "ows-account",
    path="/lookup/vendors/uuids/",
    json={                                        # exact request body to match
        "uuids": ["fff741c2-..."],
        "fetch_flags": ["TENANT_HIERARCHY"],
    },
).mock(
    return_value=httpx.Response(200, json={"vendors": [...]})
)
```

### Error response (4xx / 5xx)

```python
ows_client_mock.post(
    "ows-account",
    path="/lookup/vendors/uuids/",
    json={"uuids": ["fff741c2-..."], "fetch_flags": []},
).mock(
    return_value=httpx.Response(400, json={"code": "400", "message": "bad"})
)
```

### Network / timeout error

```python
ows_client_mock.post(
    "ows-account",
    path="/lookup/vendors/uuids/",
    json={"uuids": ["fff741c2-..."], "fetch_flags": []},
).mock(side_effect=httpx.ConnectTimeout(message="mock error"))
```

### Default Assertions

By default `OwsClientMock` asserts:

- **all registered routes were called** (`assert_all_called=True`)
- **all actual calls were mocked** (`assert_all_mocked=True`)

You can relax these per test with the `@pytest.mark.ows_client` marker:

```python
@pytest.mark.ows_client(assert_all_called=False)
async def test_something(ows_client_mock: OwsClientMock) -> None:
    ...
```
