Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

PHPUnit - Am I right in thinking I can't use mocks to test a dependency injection container?

I'm unit testing a Dependency Injection Container.

At the most basic level, I'm testing that object graph creation is happening correctly. This uses a mixture of reflection and rules loaded into the DIC.

The DIC works using class definitions and because of the dependencyless nature of mocks, am I right in thinking they are not suitable for this task?

Without using mocks, here's how one of my tests looks:

public function testObjectGraphCreation() {
    $a = $this->dic->create('A');
    $this->assertInstanceOf('B', $a->b);
    $this->assertInstanceOf('C', $a->b->c);
    $this->assertInstanceOf('D', $a->b->c->d);
    $this->assertInstanceOf('E', $a->b->c->e);
    $this->assertInstanceOf('F', $a->b->c->e->f);
} 

(obviously this chaining and public dependencies are only there for the test)

And I've defined several classes in order to make this work:

class A {
    public $b;

    public function __construct(B $b) {
        $this->b = $b;
    }
}

class B {
    public $c;

    public function __construct(C $c) {
        $this->c = $c;
    }
}

class C {
    public $d;
    public $e;

    public function __construct(D $d, E $e) {
        $this->d = $d;
        $this->e = $e;
    }
}


class D {

}
class E {
    public $f;
    public function __construct(F $f) {
        $this->f = $f;
    }
}

class F {}

Because of the nature of this test, am I right in thinking I cannot use generated mocks for this?

like image 513
Tom B Avatar asked Nov 16 '25 17:11

Tom B


1 Answers

This looks more like an integration test where you're testing the entire DI system at once. Instead, see where you can mock parts of the system--not the objects it creates. Without seeing the classes involved, I can't make an educated guess.

While you probably can't use PHPUnit's mock objects, you can roll your own one-off mocks that do the same thing. For example, to test setting a property by calling Database::setPassword with a value taken from the container you'd create a mock Database class, put the password into the container, and ask the container to create the object.

class MockDatabase {
    public $passwordSet = false;
    public function setPassword($password) {
        if ($password != 'password') {
            throw new InvalidArgumentException('Expected "password"');
        }
        $this->passwordSet = true;
    }
}

...

function testInjectProperty() {
    $container = Container::create(array(
        'password' => 'password',
    ));
    $database = $container->create('MockDatabase');
    self::assertTrue($database->passwordSet);
}
like image 108
David Harkness Avatar answered Nov 19 '25 09:11

David Harkness