Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to write custom PHPUnit assertion that behaves like built-in assertion?

How can I write a custom assertion, like assertFoo($expected, $actual), that behaves like the built-in assertions with respect to the error "stack trace"?

I currently have the following method defined (within a class that extends PHPUnit_Framework_TestCase):

public static function assertFoo($expected, $actual) {
    self::assertEquals($expected, $actual); 
}

If I call this from a test and the test fails, I get two items in the call stack:

1) PreferencesTest::testSignupTeacher
Failed asserting that 5 matches expected 3.

/vagrant/myproject/tests/integration/PreferencesTest.php:17
/vagrant/myproject/tests/integration/PreferencesTest.php:136

Line 17 is where assertFoo() calls the built-in assertEquals() and fails; line 136 is there assertFoo() is called.

If I change the test to call assertEquals() directly, I only get one:

1) PreferencesTest::testSignupTeacher
Failed asserting that 3 is true.

/vagrant/myproject/tests/integration/PreferencesTest.php:136

There's some documentation in the manual, but it doesn't seem to cover this.

like image 323
mjs Avatar asked Jul 31 '12 17:07

mjs


1 Answers

My first guess of the problem (that you're not using one of the PHPUnit_Framework_Constraint_* objects and self::assertThat) turned out to be completely irrelevant! The actual answer is that phpUnit helpfully filters away from the stack trace anything in its own codebase, and just leaves functions in user space!

The code that does this can be found in /path/to/PHPUnit/Util/Filter.php (where /path/to/ is /usr/share/php on my machine) and the functions of interest are getFilteredStacktrace and isFiltered.

If you'd like to control this behaviour, then put your custom asserts into a class derived from PHPUnit_Framework_TestCase, then derive your tests from that class. In your custom class file put a call somewhere to addFileToFilter, as shown here:

class My_Base_TestCase extends PHPUnit_Framework_TestCase{
  public static function assertFoo($expected, $actual) {
    self::assertEquals($expected, $actual); 
  }
}

PHPUnit_Util_Filter::addFileToFilter(__FILE__, 'DEFAULT');

Then in another file you have:

class CustomTest extends My_Base_TestCase{

  /** */
  public function testSomething2(){
    $this->assertFoo( 8,  5+4 );
  }
}

and it will behave just like the built-in assertEquals().

DISCLAIMER: This is using undocumented behaviour! I'll try and find out if this mechanism is going to be reasonably future-proof.

like image 138
Darren Cook Avatar answered Nov 02 '22 15:11

Darren Cook