Mocking Generators in phpunit
To mock a method call returning a generator
one can utilize phpunit's returnCallback()
method.
Suppose a class Foo
that gets some data from a Provider
class.
The Provider
class interacts with a database.
<?php
class Foo {
private $provider;
function __construct(Provider $provider) {
$this->provider = $provider;
}
function bar() {
$data = $this->provider->get();
// ...do something with data...
return $data;
}
}
class Provider {
private $pdo;
function __construct(PDO $pdo) {
$this->pdo = $pdo;
}
function get() {
$stmt = $this->pdo->prepare(
"SELECT something FROM table"
);
$stmt->execute();
while(($row = $stmt->fetch())) {
yield $row;
}
}
}
To unit test the Foo
class, one can write the following:
<?php
class FooTest extends \PHPUnit_Framework_TestCase {
private $sut;
private $provider;
function setup() {
$this->provider = $this
->getMockBuilder(Provider::class)
->disableOriginalConstructor()
->setMethods(["get"])
->getMock();
$this->sut = new Foo($this->provider);
}
function testBar() {
// TODO: mock Provider::get() method
$actual = $this->sut->bar();
// ...assertions to follow...
}
}
Because a generator returns an Iterator instead of an array, you can't
mock Provider::get()
with something like phpunit's returnValue()
.
Instead you can take advantage of phpunit's returnCallback()
like below:
// ...snip...
function testBar() {
$this->provider
->expects($this->any())
->method("get")
->will($this->returnCallback(function () {
$data = [
// mock values
];
foreach ($data as $e) {
// return a generator
yield $e;
}
}));
// ...snip...