Skip to content

Testing services

In Nameko 2, configuration was always explicitly passed as an object, which made it easy to manage in tests. The global config object needs some different ways of managing configuration in tests.

Config patching

The global config object has a patch helper object which can be used as a context manager or as a decorator. Here is an example of patching the config object in a pytest fixture:

import nameko
import pytest

@pytest.fixture
def memory_rabbit_config():
    with nameko.config.patch({"AMQP_URI": "memory://"}):
        yield

While this fixture is in place, any code accessing config['AMQP_URI'] will receive the value "memory://".

Changes to the container_factory and runner_factory fixtures.

In Nameko 2, the container_factory and runner_factory fixtures accepted config as a required argument. In Nameko 3, it is optional. If passed, nameko.config.patch is used to wrap running container. The patch is applied before container start and returned it back to its original value when exiting the test:

def test_with_container_factory(container_factory):
    container = container_factory(Service, config={"AMQP_URI": "..."})
    container.start()  # <- starts config patching scope, ending it when container
                       #    stops (exit of the container_factory fixture)
    # ...

def test_with_runner_factory(runner_factory):
    runner = runner_factory(config={"AMQP_URI": "..."}, ServiceX, ServiceY)
    runner.start()  # <- starts config patching scope, ending it when runner
                    #    stops (exit of the runner_factory fixture)
    # ...

The optional argument provides backward compatibility so that tests for Nameko extensions can be compatible with both major versions.

When creating or upgrading existing services to run on Nameko 3, it is recommended to use the new config patch pattern instead:

import nameko

@nameko.config.patch({"AMQP_URI": "memory://"})
def test_spam():
    container = container_factory(Service)
    container.start()

@nameko.config.patch({"AMQP_URI": "memory://"})
def test_service_x_y_integration(runner_factory):
    runner = runner_factory(ServiceX, ServiceY)
    runner.start()

@pytest.mark.usefixtures('rabbit_config')
def test_spam():
    container = container_factory(Service)
    container.start()

@pytest.mark.usefixtures('rabbit_config')
def test_service_x_y_integration(runner_factory):
    runner = runner_factory(ServiceX, ServiceY)
    runner.start()

Changes to the *_config fixtures

Breaking Change!

The following section describes a breaking change.

  • empty_config
  • rabbit_config
  • web_config

These fixtures no longer return the config dictionary. They simply add their configuration values to the existing config object.

A recommended pattern for setting up a config object that draws from these fixtures is as follows:

import nameko
import pytest

@pytest.fixture(autouse=True)
def config(web_config, rabbit_config):
    config = {
        # some custom testing config, defined in place or loaded from file ...
    }
    config.update(web_config)
    config.update(rabbit_config)
    with nameko.config.patch(config):
        yield
  • The --broker CLI option is deprecated in favour of --define and --config.

TODO: doesn’t actually raise a warning anymore

  • The built-in Config dependency provider is deprecated as the config can be accessed and read directly.