Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Expecting partial arrays using PHPUnit mock objects

Tags:

php

phpunit

What is the best way to test for multiple array keys in a PHPUnit mock with() clause?

For example, to test whether a method calls 2nd argument is an array containing a 'foo' key:

$this->stubDispatcher->expects($this->once())
        ->method('send')
        ->with('className', $this->arrayHasKey('foo'));

What I'd like to do is something like $this->arrayHasKey('foo', 'bar') while not actually matching the exact contents of the array.

like image 876
Kris K. Avatar asked Jul 22 '11 16:07

Kris K.


2 Answers

You can pass assertions directly to ->with() but they are named differently.

For the list take a look at the source: https://github.com/sebastianbergmann/phpunit/blob/3.5/PHPUnit/Framework/Assert.php#L2097 and following. Everything that doesn't start with "assert" and returns a new PHPUnit_Framework_*.

While i assume @David's answer works I think this approach, using logicalAnd() is a little cleaner / easier to read.

$mock->expects($this->once())->method("myMethod")->with(
    $this->logicalAnd(
        $this->arrayHasKey("foo"),
        $this->arrayHasKey("bar")
    )
);

Working example

<?php


class MyTest extends PHPUnit_Framework_TestCase {

    public function testWorks() {
        $mock = $this->getMock("stdClass", array("myMethod"));
        $mock->expects($this->once())->method("myMethod")->with(
            $this->logicalAnd(
                $this->arrayHasKey("foo"),
                $this->arrayHasKey("bar")
            )
        );
        $array = array("foo" => 1, "bar" => 2);
        $mock->myMethod($array);
    }

    public function testFails() {
        $mock = $this->getMock("stdClass", array("myMethod"));
        $mock->expects($this->once())->method("myMethod")->with(
            $this->logicalAnd(
                $this->arrayHasKey("foo"),
                $this->arrayHasKey("bar")
            )
        );
        $array = array("foo" => 1);
        $mock->myMethod($array);
    }

}

Output

phpunit assertArrayKey.php 
PHPUnit 3.5.13 by Sebastian Bergmann.

.F

Time: 0 seconds, Memory: 6.50Mb

There was 1 failure:

1) MyTest::testFails
Expectation failed for method name is equal to <string:myMethod> when invoked 1 time(s)
Parameter 0 for invocation stdClass::myMethod(array( <string:foo> => <integer:1> )) does not match expected value.
Failed asserting that an array has the key <string:bar>.

/home/edo/test/assertArrayKey.php:27
like image 176
edorian Avatar answered Nov 18 '22 00:11

edorian


You can use a callback to make multiple assertions.

$this->stubDispatcher->expects($this->once())
     ->method('send')
     ->will($this->returnCallback(function($class, $array) {
             self::assertEquals('className', $class);
             self::assertArrayHasKey('foo', $array);
             self::assertArrayHasKey('bar', $array);
     }));

Edit: And if you want to make basic assertions about some parameters and more complicated assertions about the others, you can add with().

$this->stubDispatcher->expects($this->once())
     ->method('send')
     ->with('className', $this->anything())
     ->will($this->returnCallback(function($class, $array) {
             self::assertArrayHasKey('foo', $array);
             self::assertArrayHasKey('bar', $array);
     }));

To be super clear, you should never pass $this->returnCallback() to with(). Same for returnValue() and throwException(). These are all directives that tell the mock what to do when the method is called. with() is for telling the mock what parameters it should accept.

like image 27
David Harkness Avatar answered Nov 17 '22 23:11

David Harkness