Friday, September 25, 2009

Practical testing in php part 3: assertions

This is the third part in the php testing series. You may want to checkout the previous parts or to subscribe to the feed to be notified of new posts.

Assertions are declarations that must hold true for a test to be declared successful: a test pass when it does not execute assertions or the one called are all verified correctly. Assertions are the final goal of a test, the place where you confront the expected and precalculated values of your variables with the ones that come from the system under test.
Assertions are implemented with methods and you have to make sure they are actually executed: thus, an if() construct inside a test is considered an anti-pattern as test methods should follow only one possible execution path where they find the assertions defined by the programmer.
There is also a assert() construct in php, used for enable checks on variables in production code. The assertions used in tests are a little different as they are real code (and not code passed in a string) and they do not clutter the production code but constitute a valuable part of test cases.
In phpunit there are some convenience methods which help to write expressive code and do different kind of assertions. These methods are available with a public signature on the test case class which is extended by default.

The first assertion which fails causes an exception to be raised and captured by phpunit runner. This means that if you are using an exception per test you are safe, but if you are writing test methods which contain multiple assertions, beware that the first failure will prevent the subsequent assertions from being executed. Only the assert*() calls which strictly depends on the previous ones to make sense should be placed in the same method as them.
Here is a list of the most common assertions available in phpunit: since the documentation is very good on this features I'm not going to go into the details. Most important and widely used methods are evidenced in bold.
  • assertTrue($argument) takes a boolean as a mandatory parameter and make the test fail if $argument is not true. You must pass to it a result from a method which returns booleans, such as a comparison operator result. assertFalse($argument) presents the inverse of this method behavior, failing if $argument is different from false.
  • assertEquals($expected, $actual) takes two arguments and confront them with the == operator, declaring the test failed if they do not match. The canned result should be put in the $expected argument, while the result obtained from the system under test in the $actual one: they will be shown in this order if the test fails, along with a comparison of the arguments dumps when applicable. assertNotEquals() is this method's opposite.
  • assertSame($expected, $actual) does the identical job of assertEquals(), but comparing the arguments with the === operator, which checks also the equality of variable types along with their values.
  • assertContains($needle, $haystack) searches $needle in $haystack, which can be an array or an Iterator implementation. assertNotContains() can also be very handy.
  • assertArrayHasKey($key, $array) evals if $key is in $array. It is used for both numeric and associative ones.
  • assertContainsOnly($type, $haystack) fails if $haystack contains element whose type differs from $type. $type is one of the possible result from gettype().
  • assertType($type, $variable) fails if $variable is not a $type. $type is specified as in assertContainsOnly(), or with PHPUnit types constants.
  • assertNotNull($variable) fails if $variable is the special value null.
  • assertLessThan(), assertGreaterThan(), assertGreatherThanOrEqual(), assertLessThanOrEquals() perform verifications on numbers and their names are probably self explanatory. They all take two arguments.
  • assertStringsStartsWith($prefix, $string) and assertStringsEndsWith($suffix, $string) are also self explanatory and section a string for you, avoiding the need for substr() magic in a test.
Remember that you can still make up nearly any assertion by calling a verification method and pass the result to assertTrue(). Moreover, nearly everyone of this methods support a supplemental string parameter named $message, which will be shown in the case of a failing test caused by the assertion; if you're making up a complex method for a custom assertion you may want to provide $message to assertTrue() to provide information in case the production code regress. Obviously, the custom assertion methods should be tested too.
If you want to see some code, I put up some examples on the assertion methods usage on pastebin.

I think you will start soon to use the more expressive assertions for what you are testing for: test methods should be short and easily understandable, and assertion methods which abstract away the verification burden are very beneficial. In the next parts, we'll dig into ways to reuse test code and in the annotations which phpunit recognizes to drive our test execution, such as @dataProvider and @depends.

You may want to subscribe to the feed to be informed when new posts in this series are published.

No comments: