10

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.

2
  • phpunit.de/manual/current/en/extending-phpunit.html Have you read this chapter of the manual? You can extend PHPUunit to create your own constrains. Commented Jul 22, 2011 at 17:04
  • Yeah, I've seen that. I was just hoping to not have to roll my own. Commented Jul 22, 2011 at 18:53

2 Answers 2

11

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
Sign up to request clarification or add additional context in comments.

1 Comment

How would it look like if I have a nested array, like $array = ['foo' => ['lorem' => 'ipsum]] ?
5

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.

8 Comments

Thanks, this is handy. Unfortunately, it doesn't seem to work with with().
Are you sure? I haven't combined them before, but I would expect them to work together. However, the above was intended to replace the existing with() you had.
I know, weird right? I'm on phpunit 3.5.13, and I get an expectation error like the following: Failed asserting that <string:className> is equal to PHPUnit_Framework_MockObject_Stub_ReturnCallback Object (etc)
Ah, now I see the problem. Read my code again. I am using will() to set the ReturnCallback--not with().
Right, I had noticed that. Sorry if it was not clear from the comment.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.