2

I have refactored some of my controllers to use dependency injection via property injection as recommended in the "best practices":

final class ZebraController extends Controller
{
    /**
     * @Inject
     * @var AnimalClientInterface
     */
    private $animalsRestClient;

    public function fetchAllZebras(ServerRequestInterface $req): ResponseInterface {
        // ...
    }
}

My PHP-DI configuration is configured to inject an instance of the AnimalClient object for AnimalClientInterface, which is fine in implementation code as there is only 1 real AnimalClient.

In my unit test I need to inject a MockZebraClient into this property. I cannot simply configure it like I do for the AnimalClient because other classes might be annotated similarly but need, for example, a MockTigerClient for testing.

This is my unit test:

class ZebraControllerTest extends TestCase
{
    /** @var ZebraController */
    protected $object;

    public function testFetchAllZebras(): void {
        // assertions here
    }
}

I think that using the injectOn method is the correct way to approach this problem but I don't know how to configure the container to choose the correct mock object for the correct test.

Constructor injection isn't possible due to the legacy code structure. All the controllers in the application would need to be refactored to use DI in order to change the constructor of Controller.

rink.attendant.6
  • 44,500
  • 61
  • 101
  • 156
  • Not familiar with PHP-DI, but you could always add a constructor and call it in your tests. – John V. Jan 21 '19 at 06:08
  • @JohnV. Unfortunately I can't change the method signature of the `Controller` constructor due to the legacy structure of the code. Overriding it in the child class to add more parameters isn't an option either until all controllers in the codebase are refactored to use DI. – rink.attendant.6 Jan 21 '19 at 06:43

1 Answers1

0

Maybe there is a PHP-DI specific answer, but I am unfamilar with it. It also seems defining a constructor is not allowed in your case. Given that, you can use a static pretend constructor for testing, which allows access to setting internal state:

<?php
class A {
    private $b;

    public static function construct($b) {
        $a = new A();
        $a->b = $b;
        return $a;
    }
}

class B {

}

$a = A::construct(new B());
var_dump($a);

Returns:

object(A)#2 (1) { ["b":"A":private]=> object(B)#1 (0) { } }

John V.
  • 4,652
  • 4
  • 26
  • 26