• 13
name

A PHP Error was encountered

Severity: Notice

Message: Undefined index: userid

Filename: views/question.php

Line Number: 187

Backtrace:

File: /home/prodcxja/public_html/questions/application/views/question.php
Line: 187
Function: _error_handler

File: /home/prodcxja/public_html/questions/application/controllers/Questions.php
Line: 433
Function: view

File: /home/prodcxja/public_html/questions/index.php
Line: 315
Function: require_once

name Punditsdkoslkdosdkoskdo

Expecting partial arrays using PHPUnit mock objects

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.

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
  • 10
Reply Report

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.

  • 5
Reply Report
      • 1
    • 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 is equal to PHPUnit_Framework_MockObject_Stub_ReturnCallback Object (etc)