5

In PHPUnit it quite easy to assert that two arrays contain the same value:

 $this->assertEquals( [1, 2, 3], [1, 2, 3] );

Recent versions of PHP made usage of Iterators and Generators a lot more attractive, and PHP 7.1 introduced the iterable pseudo-type. That means I can write functions to take and return iterable without binding to the fact I am using a plain old array or using a lazy Generator.

How do I assert the return value of functions returning an iterable? Ideally I could do something like

 $this->assertIterablesEqual( ['expected', 'values'], $iterable );

Is there such a function? Alternatively, is there a sane way of testing this that does not involve adding a pile of besides-the-point imperative code to my tests?

Jeroen De Dauw
  • 10,321
  • 15
  • 56
  • 79

2 Answers2

4

I think you need to wrap the Iterable first. As an example, a decorator for Iterable can be found in Iterator Garden named ForeachIterator which is decorating anything foreach-able as a Traversable:

$iterator = new ForeachIterator($iterable);
$this->assertEquals( [1, 2, 3], iterator_to_array($iterator));

Take note to the detail, that it would also consider objects Iterable in that test, which is not strict enough for a correct test.

However this should be easy to translate into a private helper method in your test to only turn arrays and traversable object - not non-traversable objects - into an iterator / array and apply the assertion:

private function assertIterablesEqual(array $expected, iterable $actual, $message = '')
{
    $array = is_array($actual) ? $actual : iterator_to_array($actual);
    $this->assertEquals($expected, $array, $message);
}

This can be further extracted into an assertion class to extend Phpunit itself.

Take note that iterator_to_array will replace entries with duplicate keys, resulting in an array with the last iteration value of duplicate keys. If you need assertion of iteration keys as well, decorating or change of the traversal method might become necessary.

Ahmad
  • 5,551
  • 8
  • 41
  • 57
hakre
  • 193,403
  • 52
  • 435
  • 836
1

You could use iterator_to_array function, as example:

 $expected = [1, 2, 3];
 $this->assertEquals( $expected, iterator_to_array($iterable) );

This works for generators also.

Hope this help

Matteo
  • 37,680
  • 11
  • 100
  • 115
  • 1
    That works if you have a `Traversable` and just care about the values yes. It does however not work for an `iterable` as `iterator_to_array` only takes `Traversable`. – Jeroen De Dauw Jun 16 '17 at 15:02