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 first.

Basic usage

Services access OData connections like this:

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:

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

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

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']}
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

Test handling of entities not found:

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

Services built on ODataAdapter are tested the same way - the mocked results are what self._invoke_odata() returns:

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}
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

Beyond unit tests, Zato ships with an in-process OData test server that speaks 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