Home » Php » php – PHPUnit: Stubbing multiple interfaces

php – PHPUnit: Stubbing multiple interfaces

Posted by: admin July 12, 2020 Leave a comment

Questions:

I’m getting to grips with PHPUnit, and have so far found it pretty easy to use, but I’ve run up against a test case that’s causing me difficulty.

I’m writing code against a set of interfaces that objects are expected to implement (some PHP ones, some self-made) and the SUT requires an input object to implement several interfaces. For example:

class MyClass implements ArrayAccess, MyInterface
{
    // ...
}

The SUT does thing such as this:

class ClassToBeTested
{
    protected $obj = NULL;

    public function __construct ($obj)
    {
        $this -> obj = $obj;
    }

    public function methodToBeTested ()
    {
        if ($this -> obj instanceof ArrayAccess)
        && ($this -> obj instanceof MyInterface)
        {
            // ...
        }
    }

    public function otherMethodUnderTest ()
    {
        if ($this -> obj instanceof ArrayAccess)
        {
            // ...
        }
        else
        if ($this -> obj instanceof MyInterface)
        {
            // ...
        }
    }
}

I can create a stub from one interface or the other, but I don’t know if you can create a stub that implements them both.

protected function setUp ()
{
    $stubField  = $this -> getMockBuilder ('ArrayAccess')
            -> getMock ();
    $this -> object = new ClassToBeTested ($stubField);
}

or

protected function setUp ()
{
    $stubField  = $this -> getMockBuilder ('MyInterface')
            -> getMock ();
    $this -> object = new ClassToBeTested ($stubField);
}

Is it possible to generate stubs from a list of interfaces, or do I have to stub a concrete class that implements the expected interfaces? That in itself is causing difficulty, because the class that needs to be stubbed itself needs another object to be passed to its constructor, and I can’t seem to get either disableOriginalConstructor () or setConstructorArgs () to work I think this is because the concrete classes in question don’t implement the constructor themselves but inherit it from a superclass. Am I missing something obvious here?

How to&Answers:

Do you have access to edit the original code? If so I would create a new interface that extends both ArrayAccess and MyInterface. That way you should be able to stub/mock an object to test the method under test.

Answer:

For the future if somebody happens to see this answer this works for me in PHPUnit 7:

$mock = $this
  ->getMockBuilder([InterfaceA::class,InterfaceB::class])
  ->getMock();

Answer:

It is not a good idea to create an interface in application codebase to make tests happy. I mean you can create this interface but it would better if you put it somewhere in the test code base. For example you can put the interface after the test case class in the file directly

To test two interfaces same time I created an interface in the test case file (it could be any other place)

interface ApiAwareAction implements ActionInterface, ApiAwareInterface
{
}

And after I did a mock of that class:

$this->getMock('Payum\Tests\ApiAwareAction');