PHP Objects, Patterns and Practice (3rd edition)

(Barry) #1
CHAPTER 18 ■ TESTING WITH PHPUNIT

Another approach is to fake the context of the class you are testing. This involves creating objects
that pretend to be the objects that do real stuff. For example, you might pass a fake database mapper to
your test object’s constructor. Because this fake object shares a type with the real mapper class (extends
from a common abstract base or even overrides the genuine class itself), your subject is none the wiser.
You can prime the fake object with valid data. Objects that provide a sandbox of this sort for unit tests
are known as stubs. They can be useful because they allow you to focus in on the class you want to test
without inadvertently testing the entire edifice of your system at the same time.
Fake objects can be taken a stage further than this, however. Since the object you are testing is likely
to call a fake object in some way, you can prime it to confirm the invocations you are expecting. Using a
fake object as a spy in this way is known as behavior verification, and it is what distinguishes a mock
object from a stub.
You can build mocks yourself by creating classes hard-coded to return certain values and to report
on method invocations. This is a simple process, but it can be time consuming.
PHPUnit provides access to an easier and more dynamic solution. It will generate mock objects on
the fly for you. It does this by examining the class you wish to mock and building a child class that
overrides its methods. Once you have this mock instance, you can call methods on it to prime it with
data and to set the conditions for success.
Let’s build an example. The UserStore class contains a method called notifyPasswordFailure(),
which sets a field for a given user. This should be called by Validator when an attempt to set a password
fails. Here, I mock up the UserStore class so that it both provides data to the Validator object and
confirms that its notifyPasswordFailure() method was called as expected:


class ValidatorTest extends PHPUnit_Framework_TestCase {
//...


public function testValidate_FalsePass() {
$store = $this->getMock("UserStore");
$this->validator = new Validator( $store );


$store->expects($this->once() )
->method('notifyPasswordFailure')
->with( $this->equalTo('[email protected]') );


$store->expects( $this->any() )
->method("getUser")
->will( $this->returnValue(array("name"=>"[email protected]",
"pass"=>"right")));


$this->validator->validateUser("[email protected]", "wrong");


}
}


Mock objects use a fluent interface, that is, a language-like structure. These are much easier to use
than to describe. Such constructs work from left to right, each invocation returning an object reference,
which can then be invoked with a further modifying method call (itself returning an object). This can
make for easy use but painful debugging.
In the previous example, I called the PHPUnit_Framework_TestCase method: getMock(), passing it
"UserStore", the name of the class I wish to mock. This dynamically generates a class and instantiates an
object from it. I store this mock object in $store and pass it to Validator. This causes no error, because
the object’s newly minted class extends UserStore. I have fooled Validator into accepting a spy into its
midst.
Mock objects generated by PHPUnit have an expects() method. This method requires a matcher
object (actually it’s of type PHPUnit_Framework_MockObject_Matcher_Invocation, but don’t worry; you can

Free download pdf