用 PHPUnit/Phake 模拟 returns PHP 中的生成器的函数

Mocking a function that returns a Generator in PHP with PHPUnit/Phake

假设我有以下界面:

interface MyInterface
{
    public function yieldData();
}

我想创建这个界面的模拟,例如像这样:

$mocked_instance = Phake::partialMock(MyInterface::class);

模拟 yield 方法的最佳方式是什么?这是我想出的最好的:

Phake::when($mocked_instance)->yieldData()->thenReturn([]);

在 PHPUnit/Phake 中有没有一种方法可以更接近函数的原始功能(即返回生成器)?

感谢您 Oliver Maksimovic 的评论,它帮助我找到了适合我的解决方案。

我决定在我的基本测试用例上创建以下函数:

/*
 * @param array @array
 *
 * @return \Generator|[]
 */
protected function arrayAsGenerator(array $array)
{
    foreach ($array as $item) {
        yield $item;
    }
}

这允许我执行以下操作:

$mocked_instance = Phake::partialMock(MyInterface::class);

$numbers = [1, 2, 3, 4, 5];

Phake::when($mocked_instance)
    ->yieldData()
    ->thenReturn($this->arrayAsGenerator($numbers));

你可以使用 Phony, a PHP mocking library with first-class support for generators:

$handle = mock(MyInterface::class);
$handle->yieldData->generates([1, 2, 3, 4, 5])->returns();

我只使用 PHPUnit,不想添加 Phake 或其他测试框架。我发现这个问题最有用的解决方案来自这篇文章:

https://www.gpapadop.gr/blog/2017/11/01/mocking-generators-in-phpunit/

但是,我不喜欢文章中语法的选择,我认为使用辅助方法更容易理解代码,generate()。在引擎盖下,它仍在使用文章中的 PHPUnit::returnCallback()

这是一个示例依赖项 class,其中包含我需要模拟的生成器方法:

class MockedClass 
{
    public function generatorFunc() : Generator
    {
        $values = [1,2,3,4];

        foreach($values as $value)
        {
            yield $value;
        }
    }
}

这里有一个 PhpUnit 测试 class,它带有 generate() 辅助方法,可实现上述文章中的解决方案:

class ExampleTest extends \PHPUnit\Framework\TestCase
{
    // Helper function creates a nicer interface for mocking Generator behavior
    protected function generate(array $yield_values)
    {
        return $this->returnCallback(function() use ($yield_values) {
            foreach ($yield_values as $value) {
                yield $value;
            }
        });
    }

    public function testMockedGeneratorExample() 
    {
        $mockedObj = $this->createMock(MockedClass::class);

        $mockedObj->expects($this->once())
            ->method('generatorFunc')
            ->will($this->generate([5,6,7,8]));

        // Run code that uses MockedClass as dependency
        // Make additional assertions as needed...
    }
}