Agile Zone is brought to you in partnership with:

I am a programmer and architect (the kind that writes code) with a focus on testing and open source; I maintain the PHPUnit_Selenium project. I believe programming is one of the hardest and most beautiful jobs in the world. Giorgio is a DZone MVB and is not an employee of DZone and has posted 637 posts at DZone. You can read more from them at their website. View Full User Profile

Practical PHP Testing Patterns: Guard Assertion

02.09.2011
| 4637 views |
  • submit to reddit

When tests are complex, or long to run, you may be tempted to insert ifs, for cycles and other logic. However, this practice may result in bugs introduced in your testing logic: after all, no one is testing the tests.

Usually, you can easily replace most of the ifs in a test with a corresponding assertion, which goes under the name of Guard Assertion. The idea behind this substitution is that the state is not exactly what we expect, we immediately declare the test failed and don't continue the execution (assertions throw exceptions when they fail.)

Self-validating tests must either pass or fail: if you're inserting an if(), chances are that you should just fail in one of the two branches (if/else or if/continue). In that case, you can write a Custom Assertion with a meaningful failure message.

The multiple purposes

Guard Assertions are also used for documentation: for example to testify that a collection you have just set up is empty, you can insert an assertion such as $this->assertEquals(0, count($emtpyArray)).

Mainly, Guard Assertions are employed to avoid the execution of other assertions or code which will fail horribly: for instance, calling a method on null or on false would break the whole test suite in PHPUnit. If you're not sure a variable contains the right object, a Guard Assertion would stop the execution before it's too late.

A variation of this pattern is called Shared Fixture State Assertion, where since we are not really certain that the state of the heavy-to-create Shared Fixture is what we want, we check it before starting our own test. For example, you can perform a SELECT query in the test database to ensure there are users in a table, or something similar.

A degenerate case of this pattern, where the Guard Assertion always fails, is $this->markTestSkipped() in an if(). These Guard Assertion's failures are counted independently (as S tests instead of E or F ones, and instead of points . that are printed for successful tests).

For example, the tests of Zend Framework which require the use of a database driver like PostGreSQL or MySQL are automatically skipped when the driver and the database instance is not available. This behavior can simply be factored out in an assertMysqlDatabaseIsAvailable() method.

Github

Starting from today, I'm pushing to Github the code contained in this articles. You won't have to copy and paste anymore, particularly in cases where a directory structure has to be replicated.

The repository you can fork or clone is https://github.com/giorgiosironi/practical-php-testing-patterns. Github is really straightforward for uploading code and if you do not want to use git, you can always get a tarball from the Url above.

Examples

The code sample shows you different use cases for Guard Assertions. Remember to provide meaningful error messages when you introduce Custom Assertions like these ones.

There are three examples: an object where a method has to be called but is not present; a collection which should be empty on creation; a database connection that must be available in order for the tests to run.

<?php

class GuardAssertionTest extends PHPUnit_Framework_TestCase
{
public function testTheObjectWhichWasnt()
{
$object = null;

// from PHPUnit 3.5 you can also use $this->assertInstanceOf()
$this->assertTrue($object instanceof MyClass, 'The object is not an instance of MyClass.');

$this->assertEquals(42, $object->getSomeField());
}

public function testTheArrayWhichIsEmptyAndThenFull()
{
$arrayObject = $this->getACollection();

$this->assertEquals(0, count($arrayObject), 'The collection is not empty.');
$arrayObject[] = 42;
$this->assertEquals(1, count($arrayObject), 'The collection does not accept new elements.');
}

public function testTheDriverWhichIsNotAvailable()
{
$this->assertMysqlDatabaseIsAvailable();
$connection = new PDO('mysql:...');
}

private function getACollection()
{
return new ArrayObject();
}

private function assertMysqlDatabaseIsAvailable()
{
// for simplicity, let's say some configuration is missing
// and we check it here
if (true) {
$this->markTestSkipped('The MySQL database for testing is not available.');
}
}
}
Published at DZone with permission of Giorgio Sironi, author and DZone MVB.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)