Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Lets say that I have the following interface:

interface MyInterface
{
    public function yieldData();
}

I want to create a mock of this interface, for example like so:

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

What is the most preferred way to mock the yield method? This is the best that I came up with:

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

Is there a way to do this in PHPUnit/Phake that more closely resembles the original functionality of the function (i.e. returning a Generator)?

like image 599
Thijs Riezebeek Avatar asked Dec 27 '16 10:12

Thijs Riezebeek


2 Answers

Thank you Oliver Maksimovic for your comment, which has helped me in finding a solution that works for me.

I've decided to create the following function on my base testcase:

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

This allows me to do the following:

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

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

Phake::when($mocked_instance)
    ->yieldData()
    ->thenReturn($this->arrayAsGenerator($numbers));
like image 98
Thijs Riezebeek Avatar answered Nov 08 '22 15:11

Thijs Riezebeek


I'm using PHPUnit only, and didn't want to mess with adding Phake or other testing frameworks. The most useful solution to this problem I found came from this article:

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

However, I don't like the choice of syntax in the article, and I think it's easier to understand the code with a helper method, generate(). Under the hood, it's still using the PHPUnit::returnCallback() like in the article.

Here's an example Dependency class with a Generator method I need to mock:

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

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

And here's a PhpUnit Test class with a generate() helper method that implements the solution in the article above:

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...
    }
}
like image 5
Adin Avatar answered Nov 08 '22 16:11

Adin