# Configuration Override in Controller Tests

**URL:** <https://discourse.laminas.dev/t/configuration-override-in-controller-tests/1597>\
**Category:** Components & MVC\
**Tags:** laminas-test\
**Created:** [May 12, 2020, 12:29pm UTC](https://discourse.laminas.dev/t/configuration-override-in-controller-tests/1597 "2020-05-12T12:29:26Z")\
**Posts on this page:** 3\
**Page:** 1

<div class="post-metadata">

**Author:** ![ryan](https://avatars.discourse-cdn.com/v4/letter/r/a8b319/32.png) [@ryan](https://discourse.laminas.dev/u/ryan)\
**Post date:** [May 12, 2020, 12:29pm UTC](https://discourse.laminas.dev/t/configuration-override-in-controller-tests/1597/1 "2020-05-12T12:29:26Z")

</div>

Hi All,

I’ve recently started adding unit tests to a ZF application that I’ve taken over from a previous developer. Mostly this has gone well (with some refactoring & migrating to Laminas) however I’ve hit a small snag trying to override some configs in my Controller tests. In my test, I’m setting up like below:

```php
<?php

namespace ApplicationTest\Controller;

use Laminas\Stdlib\ArrayUtils;
use Laminas\Test\PHPUnit\Controller\AbstractHttpControllerTestCase;
use Application\Controller\IndexController;

class IndexControllerTest extends AbstractHttpControllerTestCase {

    public function setUp(): void
    {
        $configOverrides = [
            'testing' => 'true'
        ];

        $this->setApplicationConfig(ArrayUtils::merge(
            include __DIR__. '/../../../../config/application.config.php',
            $configOverrides
        ));

        // print_r($this->getApplicationConfig());die;
	// Output: 'true' - this is good!
		
        parent::setUp();
    }
}

```

And as an example, in my `local.php`:

```php
<?php

return [
    'testing' => 'false'
];

```

Now if I access the configuration from my Module.php’s `onBootstrap` method, and dump there when running the test, the testing value is still false.

```php
<?php

namespace Application;

use Laminas\Mvc\MvcEvent;

class Module
{
    public function getConfig()
    {
        return include __DIR__. '/../config/module.config.php';
    }

    public function onBootstrap(MvcEvent $event)
    {
        $config = $event->getApplication()->getServiceManager()->get('Config');

        print_r($config['testing']);die;
        // Ouput: 'false' - but why?
    }
}

```

Is this because of how I’m accessing the config within the onBootstrap method?

The only reason I’m accessing it here and not via a factory for the controller is because the previous dev has setup the app authentication in the `onBootstrap` method, which I’ve snipped for brevity. I want to overwrite some configurations prior to running my Controller tests.

So on a side note, is the app authentication worth refactoring _out of Module.php_ for better testing?

Thanks!

---

<div class="post-metadata">

**Author:** ![samsonasik](https://yyz2.discourse-cdn.com/flex032/user_avatar/discourse.laminas.dev/samsonasik/32/777_2.png) [@samsonasik](https://discourse.laminas.dev/u/samsonasik)\
**Post date:** [May 13, 2020, 1:19pm UTC](https://discourse.laminas.dev/t/configuration-override-in-controller-tests/1597/2 "2020-05-13T13:19:28Z")

</div>

First, you was trying to override `ApplicationConfig`, not `config`. To override config, you can do like the following:

```php
<?php
        $services = $this->getApplicationServiceLocator();
        $config = $services->get('config');
        $config['testing'] = true;
        $services->setAllowOverride(true);
        $services->setService('config', $config);
        $services->setAllowOverride(false);

```

But that may won’t work to be called via controller testing as well since bootstrap first already hit and you `print_r()` in the bootstrap.

I think you can do another approach by define like the following:

```php
return [
    'testing' => filter_var(getenv('testing'), FILTER_VALIDATE_BOOLEAN) ?? false
];

```

and at phpunit.xml.dist, you can define

```xml
    <php>
        <env name="testing" value="true" />
    </php>

```

---

<div class="post-metadata">

**Author:** ![ryan](https://avatars.discourse-cdn.com/v4/letter/r/a8b319/32.png) [@ryan](https://discourse.laminas.dev/u/ryan)\
**Post date:** [May 13, 2020, 3:02pm UTC](https://discourse.laminas.dev/t/configuration-override-in-controller-tests/1597/3 "2020-05-13T15:02:33Z")

</div>

Thanks for the info Samsonasik.

For the past 24 hours I’ve been tweaking and testing different approaches and decided to refactor the code entirely.

I migrated the code from `onBootstrap` method into a Listener, and then in my test I override that Listener service in the same way as your example.

Rather than wrapping production code in actual logic (if (testing)) which was always going to be iffy practice, I thought I might as well do it right. My goal is to mock a users login to test the controllers views.

At the moment it looks like:

### IndexControllerTest.php:

```php
<?php

namespace ApplicationTest\Controller;

use Laminas\Authentication\AuthenticationService;
use Laminas\Stdlib\ArrayUtils;
use Laminas\Test\PHPUnit\Controller\AbstractHttpControllerTestCase;

/**
 * Class IndexControllerTest
 * @package ApplicationTest\Controller
 */
class IndexControllerTest extends AbstractHttpControllerTestCase {

    public function setUp(): void
    {
        $configOverrides = [];

        $this->setApplicationConfig(ArrayUtils::merge(
            include __DIR__. '/../../../../config/application.config.php',
            $configOverrides
        ));

        parent::setUp();
    }

    public function mockAuth()
    {
        $appSrvLoc = $this->getApplicationServiceLocator();

        $mockAuth = $this->getMockBuilder(AuthenticationService::class)
            ->disableOriginalConstructor()
            ->getMock();

        $mockAuth->expects($this->any())
            ->method('hasIdentity')
            ->willReturn(true);

        $mockAuth->expects($this->any())
            ->method('getIdentity')
            ->willReturn(['id' => 1]);

        $appSrvLoc->setAllowOverride(true);
        $appSrvLoc->setService(AuthenticationService::class, $mockAuth);
        $appSrvLoc->setAllowOverride(false);
    }

    public function testHomeCanBeAccessed()
    {
        $this->mockAuth();

        $this->dispatch('/', 'GET');
        $this->assertResponseStatusCode(200);
        $this->assertModuleName('application');
        $this->assertControllerName(IndexController::class);
        $this->assertControllerClass('IndexController');
        $this->assertMatchedRouteName('home');
    }
}

```

And then I pass through `AuthenticationService` in the constructor of my Listener:

### SessionListener.php

```php
<?php

namespace Application\Listener;

use Laminas\Authentication\AuthenticationService;
use Laminas\EventManager\Event;
use Laminas\EventManager\EventManagerInterface;
use Laminas\EventManager\ListenerAggregateInterface;
use Laminas\Http\Response;
use Laminas\Mvc\MvcEvent;

class SessionListener implements ListenerAggregateInterface {

    /** @var AuthenticationService */
    protected AuthenticationService $authService;

    /** @var array */
    protected array $listeners = [];

    /**
     * SessionListener constructor.
     * @param AuthenticationService $authenticationService
     */
    public function __construct(AuthenticationService $authenticationService)
    {
        $this->authService = $authenticationService;
    }

    /**
     * @return AuthenticationService
     */
    public function getAuthService(): AuthenticationService
    {
        return $this->authService;
    }

    /**
     * @return array
     */
    public function getListeners(): array
    {
        return $this->listeners;
    }

    /**
     * Attach event to listener
     *
     * @param EventManagerInterface $events
     * @param int $priority
     */
    public function attach(EventManagerInterface $events, $priority = 1)
    {
        $events->attach(MvcEvent::EVENT_DISPATCH,
            [$this, 'confirmSession'], 1
        );
    }

    /**
     * Confirm user has session
     *
     * @param Event $event
     * @return bool|Response
     */
    public function confirmSession(Event $event)
    {
        $controller = $event->getTarget();

        if (!$this->getAuthService()->hasIdentity()) {

            // Invalid user - redirect to the Login page.
            return $controller->redirect()->toRoute('login');
        }
        

        return true;
    }

    /**
     * Detach listeners
     *
     * @param EventManagerInterface $events
     */
    public function detach(EventManagerInterface $events)
    {
        foreach ($this->listeners as $index => $listener) {
            $events->detach($listener);
            unset($this->listeners[$index]);
        }
    }
}

```

I’ve got a little more validation to add in i.e. route permission access but I much prefer this code method for code separation.

I then attach the Listener in `module.config.php` like so:

```php
<?php

namespace Application;

use Laminas\Authentication\AuthenticationService;
use Laminas\Router\Http\Literal;
use Laminas\Log\Logger;
use Laminas\Router\Http\Segment;
use Laminas\ServiceManager\Factory\InvokableFactory;

return [
    'controllers' => [
        'factories' => [
            Controller\IndexController::class => Controller\Factory\IndexControllerFactory::class
        ],
    ],
    'listeners' => [
        Listener\SessionListener::class
    ],
    'service_manager' => [
        'factories' => [
            AuthenticationService::class => InvokableFactory::class,
            Listener\SessionListener::class => Listener\Factory\SessionListenerFactory::class
        ]
    ]
    // ...
];

```

## Current Issue

Hijacking my own thread here but weirdly, this approach **was** working earlier, however I just ran my tests again and now it’s failing. It looks like my mock AuthenticationService is no longer being fired and hasIdentity() is false, forcing a redirect…

`Failed asserting response code "200", actual status code is "302"`

Is there any better practice for this situation that’s more consistent or am I building these mocks incorrectly? I’ve got a similar mock for a curl wrapper to fake an API response which is still working fine.
