Imagine we have a model called Accounts which contains a static find() method.
class Accounts extends MyModel
{
public static function find($param)
{
// ...
}
}
Unless you're using a PHP framework built for testing like Laravel, testing this method from within a controller action can be a quite frustrating thing to do.
class AuthController extends MyController
{
public function loginAction()
{
// ...
$account = Accounts::find(array('username'=> $username));
$result = $account->validate($password);
// ...
}
}
How on earth can you test this?
Let's set up our PHPUnit_Framework_TestCase…
class AuthControllerTest extends PHPUnit_Framework_TestCase
{
public function testSomeAssertionShouldBeTrue()
{
$accounts = $this->getMock('\Accounts', array('validate', 'find'));
$accounts->expects($this->once())->method('validate')->will($this->returnValue(true));
// The following line set the static method expectations
$accounts::staticExpects($this->any())->method('find')->will($this->returnValue($accounts));
$controller = new AuthController();
$result = $controller->loginAction();
$this->assertTrue($result);
// Hold on! How do it get the mock object in the action?!?
}
It might not be as hard as you might think! (If you're willing to do a bit refactoring…)
class AuthController extends MyController
{
private $model;
public function getAccounts()
{
if (is_null($accounts)){
$this->accounts = new Accounts();
}
public function setAccounts(Accounts $accounts)
{
$this->accounts = $accounts;
}
public function loginAction()
{
// ...
$repo = $this->getAccounts();
$account = $repo::find(array('username'=> $username));
$result = $account->validate($password);
// ...
}
}
That's great news! This means we can inject the mocked object in our controller!
class AuthControllerTest extends PHPUnit_Framework_TestCase
{
public function testSomeAssertionShouldBeTrue()
{
$accounts = $this->getMock('\Accounts', array('validate', 'find'));
$accounts->expects($this->once())->method('validate')->will($this->returnValue(true));
// The following line set the static method expectations
$accounts::staticExpects($this->any())->method('find')->will($this->returnValue($accounts));
$controller = new AuthController();
// inject the mocked object in the controller
$controller->setAccounts($accounts);
$result = $controller->loginAction();
$this->assertTrue($result);
// Tada!
}