Nameko v3.0.0rc11: Passing config to worker_factory

Hi,

I am using v3.0.0rc11. My service is defined as below:

import logging

from nameko import config
from nameko.rpc import rpc

class MyService:
    name = config.get('SERVICE_NAME')
    logger = logging.getLogger(__name__)

    @rpc
    def sum(self, num1, num2):
        self.logger.info(f'Num1: {num1}, Num2: {num2}')
        return num1 + num2

I have written my unit test thus:

from nameko.testing.services import worker_factory

from MySvc.myservice import MyService

class TestMyService:
    def setup_method(self):
        self.cfg = {
            'SERVICE_NAME' : 'MyService'
        }

    def test_sum(self, container_factory):
        cfg = {
            'SERVICE_NAME' : 'MyService'
        }
        
        svc = worker_factory(MyService, config=cfg)

        assert( svc.name == 'MyService' )
        assert( svc.sum(2,3) == 5 )

The unit test fails, with this error:

nameko.exceptions.ExtensionNotFound: DependencyProvider(s) 'dict_keys(['config'])' not found on <class 'MySvc.myservice.MyService'>.

What am I doing wrong? How can I pass in config to worker_factory so the config gets passed in to service properly?

Thanks for all the help!

Radha.

Nameko is complaining because worker_factory expects its arguments to be dependencies to be injected. Service classes don’t need define a config dependency provider in Nameko 3, because the config object is a global.

You can update the global config object directly from your test. In fact, the config object has a patch method for this purpose. I’ve written some docs on this here Testing services -.

Your service is reading from the config object at import time, so you need to make sure your test populates the desired configuration beforehand. Moving the import inline is sufficient, or you can use importlib.reload.

This works:

from nameko.testing.services import worker_factory
from nameko import config

import service  # module where MyService is defined

import pytest
import importlib

class TestMyService:

    @pytest.fixture
    def set_config(self):
        with config.patch({"SERVICE_NAME": "MyService"}):
            yield

    def test_sum(self, set_config):
        importlib.reload(service)
        svc = worker_factory(service.MyService)

        assert( svc.name == 'MyService' )
        assert( svc.sum(2,3) == 5 )

Thanks Matt! That works!

Another question related to MyService above: What is the best practice to set the name of a service from a config object?

When service gets imported at import service # module where MyService is defined line, config object is None (config object is not patched yet here), and the line name = config.get(‘SERVICE_NAME’) runs into an error.

Deriving the service name from config is fine. It’s one of the advantages of config being a global.

It shouldn’t matter if config.get(‘SERVICE_NAME’) is None temporarily, as long as you reimport the module after the patch is in place.

What is the error you’re seeing?