使用PHPUnit / Phake模拟一个在PHP中返回Generator的函数

时间:2016-12-27 10:22:14

标签: php unit-testing phpunit generator phake

假设我有以下界面:

interface MyInterface
{
    public function yieldData();
}

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

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

模拟yield方法的首选方法是什么?这是我提出的最好的:

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

有没有办法在PHPUnit / Phake中做到这一点,它更接近函数的原始功能(即返回一个Generator)?

3 个答案:

答案 0 :(得分:3)

感谢您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));

答案 1 :(得分:0)

您可以使用Phony,一个带有first-class support for generators的PHP模拟库:

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

答案 2 :(得分:0)

我仅使用PHPUnit,不想与添加Phake或其他测试框架混为一谈。我发现此问题最有用的解决方案来自本文:

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

但是,我不喜欢本文中语法的选择,而且我认为使用辅助方法generate()更容易理解代码。在幕后,它仍然像本文中一样使用PHPUnit::returnCallback()

这是一个示例类,它带有我需要模拟的Generator方法:

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

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

这是一个带有generate()帮助方法的PhpUnit Test类,该方法实现了以上文章中的解决方案:

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...
    }
}