Keith Davis
Keith Davis

Reputation: 351

phpunit: why does this assertContains pass?

My test should fail, but it passes:

public function test_getValidProviderCodes(){
    $aTest = PRIDE\Reflection::executeStaticMethodForClassName(Apps_DoctorsReports::class, "getValidProviderCodes");
    print_r($aTest);
    $this->assertContains("xxxxxxxxxxxxxx", $aTest);
}

Output:

Testing started at 8:53 AM ...
PHPUnit 4.6.6 by Sebastian Bergmann and contributors.

Configuration read from C:\inetpub\Intranet_Local\phpunit\phpunit.xml

Array
(
    [0] => 0
    [1] => 1
    [2] => 2
    [3] => MAYER
    [4] => MAY00
    [5] => MAYERIC
    [6] => COH00
    [7] => COH01
    [8] => POWELL
    [9] => POW00
    [10] => JOHN00
    [11] => FINO
    [12] => POL01
    [13] => NONAP
    [14] => RAYE00
    [15] => HOPS00
    [16] => CHAH00
)
 - 


Time: 1.24 seconds, Memory: 8.25Mb

OK (1 test, 1 assertion)

The value "xxxxxxxxxxxxxx" is obviously not in that array. I've used this function hundreds of times and have never seen this behavior.

(If I change $aTest to [], the test does fail.)


This is another test run:

public function test_getValidProviderCodes(){
    $aTest = PRIDE\Reflection::executeStaticMethodForClassName(Apps_DoctorsReports::class, "getValidProviderCodes");
    $this->assertContains("S01", implode(", ", $aTest));
}

Output:

Testing started at 9:04 AM ...
PHPUnit 4.6.6 by Sebastian Bergmann and contributors.

Configuration read from C:\inetpub\Intranet_Local\phpunit\phpunit.xml


Failed asserting that '0, 1, 2, M01, M03, M04, M05, N02, C01, C02, C03, C04, P01, P02, P03, P04, P05, P06, P07, R01, H01, J01, J02' contains "S01".
 C:\inetpub\Intranet_Local\phpunit\library\classes\apps\DoctorsReportsTest.php:61



Time: 1.54 seconds, Memory: 8.75Mb


FAILURES!
Tests: 1, Assertions: 1, Failures: 1.

Upvotes: 5

Views: 3392

Answers (2)

Nathan Arthur
Nathan Arthur

Reputation: 9096

As of January 2017, PHPUnit appears to have a regression bug that causes this behavior, and for which setting the checkForNonObjectIdentity flag is not a solution.

You can, however, achieve assertContains-type behavior with strict type checking this way:

$this->assertTrue( in_array( $arguments, $calls, TRUE ) );

The third argument to in_array is the strict flag, which forces type checking when set to TRUE.

I use this technique in a helper function that checks if a list of args is present in a list of mock method calls. I had to change this function for strict typing after I discovered that FALSE and [] where being treated as equal, therefore causing tests to pass which should have failed.

protected function assertCalledWith( $mock, $method, $arguments ) {
    $calls = $mock->getCalls( $method );
    $mockName = get_class( $mock );
    $error = "Failed asserting that $mockName->$method() was called with specified args.";
    $this->assertTrue( in_array( $arguments, $calls, TRUE ), $error );
}

Upvotes: 1

Matteo
Matteo

Reputation: 39380

The assertContains can check for Object Identity also (the default behaviour). So for skip this, you need to pass an optional flag to the method. This problem is already covered here on github.

So to make fails your test, simply pass the last optional parameter checkForNonObjectIdentity the value true as follow:

public function test_getValidProviderCodes(){

    $aTest =
        array(
            0,1,2,'MAYER'
        );
    print_r($aTest);
    $this->assertContains(
        "xxxxxxxxxxxxxx",
        $aTest,
        'message',
        false,true,true);

}

with the output:

Failed asserting that an array contains 'xxxxxxxxxxxxxx'.

Hope this help

Upvotes: 3

Related Questions