# Testing OData connections

OData mocks - entity sets, single entities, adapters and the live OData test server.

When your Zato service reads customers from Business Central, queries sales orders in S/4HANA, or looks up employees in SuccessFactors, you need to test that logic without a real backend. This page shows how to mock OData responses so your tests run fast and don't require access to the actual systems.

**Note:** If you're new to unit testing with Zato, check the [tutorial](https://zato.io/docs/api-testing/tutorial.html) first.

## Basic usage {#basic-usage}

Services access OData connections like this:

```python
from zato.server.service import Service

class GetCustomersByCity(Service):
    name = 'odata.customers.by-city'
    input = 'city'

    def handle(self):
        conn = self.odata['Business Central']
        customers = conn.read('customers',
            filter=f"city eq '{self.request.input.city}'",
            orderby='displayName',
        )

        self.response.payload = {
            'customers': [{'name': c['displayName'], 'city': c['city']} for c in customers]
        }
```

Mock the OData results in your test:

```python
from zato_testing import ServiceTestCase
from myapp.services import GetCustomersByCity

class TestGetCustomersByCity(ServiceTestCase):

    def test_returns_customers(self):

        # Configure OData results with the odata: prefix
        self.set_response('odata:Business Central', [
            {'id': 'c1', 'displayName': 'Adatum Corporation', 'city': 'London'},
            {'id': 'c2', 'displayName': 'Trey Research', 'city': 'London'},
        ])

        service = self.invoke(GetCustomersByCity, city='London')

        self.assertEqual(len(service.response.payload['customers']), 2)
        self.assertEqual(service.response.payload['customers'][0]['name'], 'Adatum Corporation')
```

Note the `odata:` prefix to distinguish OData connections from REST connections.

## Single entities {#single-entities}

Services that read one entity by key use .get - mock a single dict:

```python
class GetCustomer(Service):
    name = 'odata.customer.get'
    input = 'customer_id'

    def handle(self):
        conn = self.odata['Business Central']
        customer = conn.get('customers', self.request.input.customer_id)

        self.response.payload = {'name': customer['displayName']}
```

```python
class TestGetCustomer(ServiceTestCase):

    def test_returns_customer(self):

        self.set_response('odata:Business Central', {
            'id': 'c1', 'displayName': 'Adatum Corporation', 'city': 'London'
        })

        service = self.invoke(GetCustomer, customer_id='c1')

        self.assertEqual(service.response.payload['name'], 'Adatum Corporation')
```

## Empty results {#empty-results}

Test handling of entities not found:

```python
class TestNoCustomers(ServiceTestCase):

    def test_handles_empty_set(self):

        self.set_response('odata:Business Central', [])

        service = self.invoke(GetCustomersByCity, city='Atlantis')

        self.assertEqual(service.response.payload['customers'], [])
```

## Adapters {#adapters}

Services built on ODataAdapter are tested with the same `set_response` call - the mocked results are what `self._invoke_odata()` returns:

```python
from zato.server.service import ODataAdapter

class CustomersByCity(ODataAdapter):
    name = 'odata.adapter.customers-by-city'

    conn_name  = 'Business Central'
    entity_set = 'customers'
    filter     = "city eq '{city}'"

    def handle(self):
        items = self._invoke_odata()
        self.response.payload = {'items': items}
```

```python
class TestCustomersByCity(ServiceTestCase):

    def test_maps_items(self):

        self.set_response('odata:Business Central', [
            {'id': 'c1', 'displayName': 'Adatum Corporation', 'city': 'London'},
        ])

        service = self.invoke(CustomersByCity, city='London')

        self.assertEqual(len(service.response.payload['items']), 1)
```

## Live testing with the OData test server {#live-testing-with-the-odata-test-server}

Beyond unit tests, Zato ships with an in-process OData test server that supports both V2 and V4 and simulates the auth and validation behavior of S/4HANA, SuccessFactors, Dynamics 365 Finance and Operations and Business Central - including SAP CSRF token exchanges, OAuth2 token endpoints and server-driven paging. It records every request it receives, which lets integration tests assert both the mapped output and what actually went over the wire.

## Learn more {#learn-more}

- [API testing](https://zato.io/docs/api-testing/) - Tests in plain English, run against real or mocked connections
- [API testing tutorial](https://zato.io/docs/api-testing/tutorial.html) - A complete test suite, from the first scenario to the report
- [Testing REST connections](https://zato.io/docs/api-testing/rest.html) - Mock outgoing REST calls and assert on what your service sent
- [Testing SQL connections](https://zato.io/docs/api-testing/sql.html) - Mock database queries and the rows they return
- [Testing LDAP connections](https://zato.io/docs/api-testing/ldap.html) - Mock directory lookups and the entries they find
- [Testing Microsoft 365](https://zato.io/docs/api-testing/ms365/) - Mock mailboxes, calendars, SharePoint, Teams and the rest
- [Testing service invocations](https://zato.io/docs/api-testing/services.html) - Services that call other services, with the callees mocked
- [Testing Jira connections](https://zato.io/docs/api-testing/jira.html) - Mock Jira API calls and the issues they return
- [Tests in CI/CD](https://zato.io/docs/api-testing/ci-cd.html) - Running the suite in a pipeline and reading its output
