Skip to content
This repository

Support Traversable interfaces in mock objects #604

Closed
webmozart opened this Issue July 13, 2012 · 10 comments

4 participants

Bernhard Schussek Sebastian Bergmann Volker Dusch Benjamin Eberlei
Bernhard Schussek

Currently, PHPUnit fails when trying to mock an interface that implements \Traversable because PHP expects implementations of that interface to also either implement \Iterator or \IteratorAggregate.

<?php

interface MyInterface extends \Traversable {}

// doesn't work
$this->getMock('MyInterface');

The fix currently is to extend the interface for testing purposes and to add one of the two required interfaces (Note: The order of the interfaces is important here!)

<?php

interface TestMyInterface extends \Iterator, MyInterface {}

// works
$this->getMock('TestMyInterface');

IMO PHPUnit should be able to add that interface automatically in such situations, i.e.:

  • when the mock is created for an interface or an abstract class, and
  • that interface/abstract class implements \Traversable, but not \Iterator nor \IteratorAggregate
Volker Dusch
Collaborator

He,

thanks for the detailed bug report. I'm currently failing to understand which use case requires one to extend the traversable interface.

It should work to throw that into the mock building but I'd like to state a use case in the tests for that.

Bernhard Schussek

I'm currently failing to understand which use case requires one to extend the traversable interface.

Any interface whose implementation is supposed to be traversable needs to extend it. A very simple example:

<?php
interface ArrayInterface extends \ArrayAccess, \Countable, \Traversable {}

// The implementation can chose to implement \Iterator...
class Array1 implements \Iterator, ArrayInterface
{
    ...
}

// or \IteratorAggregate
class Array2 implements \IteratorAggregate, ArrayInterface
{
    ...
}

Interfaces usually specify a contract, without specifying how that contract should be implemented. These core interfaces are special in that \Traversable specifies the contract ("This object should be iterable") while \Iterator and \IteratorAggregate specify how that contract is implemented. Consequently, no userland interface should them, but always \Traversable IMHO.

Volker Dusch
Collaborator

So what the interface is saying there is "I can foreach over things that implement this but i don't care how the implementing class makes that happen (Iterator or IteratorAggregate).

Makes sense to me. Thanks for explaining :)

Bernhard Schussek

Exactly :)

Sebastian Bergmann

The PHP Manual states "This is an internal engine interface which cannot be implemented in PHP scripts. Either IteratorAggregate or Iterator must be used instead." While the documentation continues with "When implementing an interface which extends Traversable, make sure to list IteratorAggregate or Iterator before its name in the implements clause.", I do not think that Traversable should be used in userland at all and therefore am against adding support for this edge-case to PHPUnit.

Sebastian Bergmann sebastianbergmann closed this October 06, 2012
Bernhard Schussek

@sebastianbergmann IMHO you are misquoting the manual. The manual talks about implementing Traversable, not extending it.

As I have shown above, there is a very clear and valid use case for extending Traversable in other interfaces (without contradicting the manual you quoted).

Benjamin Eberlei

If the traversable interface was internal to the engine, it would not be exported to userland.

Sebastian Bergmann sebastianbergmann reopened this October 07, 2012
Sebastian Bergmann

I would accept a pull request for phpunit-mock-objects that implements the fix described in the original ticket.

Sebastian Bergmann sebastianbergmann closed this October 07, 2012
Bernhard Schussek webmozart referenced this issue in sebastianbergmann/phpunit-mock-objects October 07, 2012
Closed

Support Traversable interfaces in mock objects #103

Bernhard Schussek

Ok. I opened a corresponding ticket in that repository.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.