PHP Objects, Patterns and Practice (3rd edition)

(Barry) #1
CHAPTER 18 ■ TESTING WITH PHPUNIT

self::fail( "Exception should have been thrown" );
} catch ( Exception $e ) {
$const = $this->logicalAnd(
$this->logicalNot( $this->contains("bob stevens")),
$this->isType('array')
);
self::AssertThat( $this->store->getUser( "[email protected]"), $const );
}
}


This test adds a user to the UserStore object and then adds a second user with the same e-mail
address. The test thereby confirms that an exception is thrown with the second call to addUser(). In the
catch clause, I build a constraint object using the convenience methods available to us. These return
corresponding instances of PHPUnit_Framework_Constraint. Let’s break down the composite constraint
in the previous example:


$this->contains("bob stevens")


This returns a PHPUnit_Framework_Constraint_TraversableContains object. When passed to
AssertThat, this object will generate an error if the test subject does not contain an element matching
the given value ("bob stevens"). I negate this, though, by passing this constraint to another:
PHPUnit_Framework_Constraint_Not. Once again, I use a convenience method, available though the
TestCase class (actually through a superclass, Assert).


$this->logicalNot( $this->contains("bob stevens"))


Now, the AssertThat assertion will fail if the test value (which must be traversable) contains an
element that matches the string "bob stevens". In this way, you can build up quite complex logical
structures. By the time I have finished, my constraint can be summarized as follows: “Do not fail if the
test value is an array and does not contain the string "bob stevens".” You could build much more
involved constraints in this way. The constraint is run against a value by passing both to AssertThat().
You could achieve all this with standard assertion methods, of course, but constraints have a couple
of virtues. First, they form nice logical blocks with clear relationships among components (although
good use of formatting may be necessary to support clarity). Second, and more importantly, a constraint
is reusable. You can set up a library of complex constraints and use them in different tests. You can even
combine complex constraints with one another:


$const = $this->logicalAnd(
$a_complex_constraint,
$another_complex_constraint );


Table 18–2 shows the some of the constraint methods available in a TestCase class.
Free download pdf