7

I'm trying to set up a unit test based on UnitTestCase in Drupal 8 that uses some config variables. Unfortunately I was not able to find a way to insert those config variables into the system in the setup (searched Drupal documentation and any Drupal unit test example involving config), so the function being tested always works with null config and fails. I am trying to test individual functions in isolation, so a browser test is not really appropriate for this.

I have simplified the problem to the minimum code necessary to explain/reproduce the issue. Here are the three components. Where in the mock/setup do I insert the array('bucketName' => 'test_bucket') and other properties so that it can be read from the tested function?

Function:

public function example() {
    return \Drupal::config('my_module.job')->get('my_module.bucketName');
}

Test function:

public function testExample() {
    $myModuleHelper = new MyModuleHelper();
        $expected_config = array(
            'bucketName' => 'test_bucket',
        );

    $this->assertEquals($expected_config, $myModuleHelper->example());
}

Test setup:

public function setUp() {
    $this->config = $this->getMockBuilder('\Drupal\Core\Config\ImmutableConfig')
        ->disableOriginalConstructor()
        ->getMock();

    $this->configFactory = $this->getMockBuilder('\Drupal\Core\Config\ConfigFactory')
        ->disableOriginalConstructor()
        ->getMock();
    $this->configFactory->expects($this->any())
        ->method('get')
        ->with('my_module.job')
        ->willReturn($this->config);

    $this->container = new ContainerBuilder();
    $this->container->set('config.factory', $this->configFactory);
    \Drupal::setContainer($this->container);
}

Test response:

null does not match expected type "array".

2 Answers 2

6

Your mock of the Config object needs to also have a ::get method and return the appropriate value using your array. The core UnitTestCase class provides ::getConfigFactoryStub(array $configs), which does all of this automatically.

In your setup, create the config factory like this:

$this->configFactory = $this->getConfigFactoryStub([
  'mymodule.job' => ['bucketName' => 'test_bucket'],
]);

Unfortunately, the mocked config does not quite work like a real config object; namely, it doesn't accept dotted keys but only top-level keys. The following will not work:

$this->configFactory = $this->getConfigFactoryStub([
  'mymodule.settings' => [
    'key' => [
      'subkey' => [
        'subsubkey' => 'value',
      ],
    ],
  ],
]);
$this->container = new ContainerBuilder();
$this->container->set('config.factory', $this->configFactory);
\Drupal::setContainer($this->container);

\Drupal::config('mymodule.settings')->get('key.subkey.subsubkey');

This bug report shows a workaround: https://www.drupal.org/node/2862248

Update: The above problem was fixed in Drupal 8.5.0, so the workaround is no longer needed.

1

If you are using KernelTestBase (for "functional" tests that run on a bootstrapped Drupal and can use services), then see this answer for details on how to use KernelTestBase::config() to set configuration values that are accessible via the injected config.factory service.

Note that this won't work for tests extending UnitTestCase as Drupal isn't fully set up.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.