| Prev | Next |
Sometimes you need to check that an object has been called correctly.
Here is an example: suppose we want to test that the correct method,
update() in our example, is called on an object that
observes another object.
In Example 10.1
we first use the getMock() method that is provided by
the PHPUnit_Framework_TestCase class (see Table 20.7) to set up a mock object for the
Observer. Since we give an array as the second
(optional) parameter for the getMock() method, only
the update() method of the Observer
class is replaced by a mock implementation.
We then use the Fluent Interface
that PHPUnit provides to specify behavior and expectations for the mock.
In essence, this means that you do not need to create several temporary
objects -- for instance one to specify that you expect the
update() method to be called and another for the
expected parameter -- and wire them together afterwards to configure
expectations. Instead, you chain method calls as shown in the example.
This leads to more readable and "fluent" code.
Example 10.1: Testing that a methods gets called once and with a specified parameter
<?php
require_once 'PHPUnit/Framework.php';
class ObserverTest extends PHPUnit_Framework_TestCase
{
public function testUpdateIsCalledOnce()
{
// Create a Mock Object for the Observer class
// mocking only the update() method.
$observer = $this->getMock('Observer', array('update'));
// Set up the expectation for the update() method
// to be called only once and with the string 'something'
// as its parameter.
$observer->expects($this->once())
->method('update')
->with($this->equalTo('something'));
// Create a Subject object and attach the mocked
// Observer object to it.
$subject = new Subject;
$subject->attach($observer);
// Call the doSomething() method on the $subject object
// which we expect to call the mocked Observer object's
// update() method with the string 'something'.
$subject->doSomething();
}
}
?>
Table 10.1 lists the available matchers that can be used to express expectations on how often a mocked method is to be executed.
Table 10.1. Matchers
| Matcher | Meaning |
|---|---|
PHPUnit_Framework_MockObject_Matcher_AnyInvokedCount any() | Returns a matcher that matches when the method it is evaluated for is executed zero or more times. |
PHPUnit_Framework_MockObject_Matcher_InvokedCount never() | Returns a matcher that matches when the method it is evaluated for is never executed. |
PHPUnit_Framework_MockObject_Matcher_InvokedAtLeastOnce atLeastOnce() | Returns a matcher that matches when the method it is evaluated for is executed at least once. |
PHPUnit_Framework_MockObject_Matcher_InvokedCount once() | Returns a matcher that matches when the method it is evaluated for is executed exactly once. |
PHPUnit_Framework_MockObject_Matcher_InvokedCount exactly(int $count) | Returns a matcher that matches when the method it is evaluated for is executed exactly $count times. |
PHPUnit_Framework_MockObject_Matcher_InvokedAtIndex at(int $index) | Returns a matcher that matches when the method it is evaluated for is invoked at the given $index. |
The constraints that can be used together with these matchers can be found in Table 20.2.
Alternatively, you can test the Subject implementation
without the use of Mock Objects and apply the Self Shunt Pattern
instead. This uses the test-case object itself as a stub. The term
self-shunting is taken from the medical practice of installing a tube
that takes blood from an artery and returns it to a vein to provide a
convenient place for injecting drugs.
First, we make our test-case class an implementor of Observer,
an interface to be implemented by objects that want to observe
Subject:
class ObserverTest extends PHPUnit_Framework_TestCase implements Observer
{
}
Next, we implement the one Observer method,
update(), to check that it is called when the state
of the observed Subject object changes:
public $wasCalled = FALSE;
public function update(Subject $subject)
{
$this->wasCalled = TRUE;
}
Now, we can write our test. We create a new Subject
object and attach the test object to it as an observer. When the state
of the Subject changes -- for instance, by calling its
doSomething() method -- the Subject
object has to call the update() method on all objects
that are registered as observers. We use the $wasCalled
instance variable that is set by our implementation of
update() to check whether the Subject
object does what it is supposed to do:
public function testUpdate()
{
$subject = new Subject;
$subject->attach($this);
$subject->doSomething();
$this->assertTrue($this->wasCalled);
}
Notice that we create a new Subject object instead of
relying on a global instance. Stubbing encourages this style of design.
It reduces the coupling between objects and improves reuse.
If you are not familiar with the self-shunt pattern, the tests can be hard to read. What is going on here? Why is a test case also an observer? Once you get used to the idiom, the tests are easy to read. Everything you need to understand the test is in one class.
Tests that only test one thing are more informative than tests where failure can come from many sources. How can you isolate your tests from external influences? Simply put, by replacing the expensive, messy, unreliable, slow, complicated resources with stubs that are automatically generated for the purpose of your tests. For example, you can replace what is in reality a complicated computation with a constant, at least for the purposes of a single test.
Stubs solve the problem of allocating expensive external resources.
For example, sharing a resource, such as a database connection, between
tests by using the PHPUnit_Extensions_TestSetup
decorator helps, but not using the database for the purposes of the tests
at all is even better.
Example 10.2 shows how to stub method calls and set up return values.
Example 10.2: Stubbing a method call
<?php
require_once 'PHPUnit/Framework.php';
class StubTest extends PHPUnit_Framework_TestCase
{
public function testStub()
{
$stub = $this->getMock('SomeClass');
$stub->expects($this->any())
->method('doSomething')
->will($this->returnValue('foo'));
// Calling $stub->doSomething() will now return 'foo'.
}
}
?>
Table 10.2 lists the methods that are available to configure the return values for stubbed method calls.
Table 10.2. Stubs API
| Method | Meaning |
|---|---|
PHPUnit_Framework_MockObject_Stub_Return returnValue(mixed $value) | Sets the return value to $value for all invocations of the method. |
PHPUnit_Framework_MockObject_Stub_ConsecutiveCalls onConsecutiveCalls(mixed $value, ...) | Sets up different return values for consecutive invocations of the method. |
Alternatively, you can write the stub yourself and improve your design
along the way. Widely used resources are accessed through a single façade,
so you can easily replace the resource with the stub. For example,
instead of having direct database calls scattered throughout the code,
you have a single Database object, an implementor of
the IDatabase interface. Then, you can create a stub
implementation of IDatabase and use it for your
tests. You can even create an option for running the tests with the
stub database or the real database, so you can use your tests for both
local testing during development and integration testing with the real
database.
Functionality that needs to be stubbed out tends to cluster in the same object, improving cohesion. By presenting the functionality with a single, coherent interface, you reduce the coupling with the rest of the system.
| Prev | Next |
Copyright © 2005-2010 Sebastian Bergmann.