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
Other configuration related changes
- 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.