Mocking Internal PHP Functions
A problem that I've run into during the unending quest for complete test coverage is mocking of internal PHP functions. Unit tests are easy to write as long as a class method returns everything it does, but when a method starts to call out to certain internal PHP functions things get difficult. It's hard to verify that a function was called correctly or to fix the response to be a testable constant. One way that I've been working around this is to use namespaced functions.
In one of my released packages, Archangel, a mailing utility, the result of one of the methods is a call to PHP's 'mail' function. This call is returned from the method, which doesn't give a tester much information - 'mail' returns with a boolean on whether or not the message was sent, something that is more dependent on the underlying server implementation of 'sendmail' then correct usage. What I wanted to unit test was whether or not the call was made correctly.
namespace Jacobemerick\Archangel;
class Archangel
{
...
public function send()
{
...
return mail($recipients, $subject, $message, $headers);
}
}
In this case, I didn't care what the response of 'sendmail' was, I wanted to know if 'mail' was called with the correct parameters. (There may be a way to capture the outgoing message and test the body of it, though I'm pretty sure that's outside the scope of a unit test.) So I created a file of function overrides, set it up to autoload in a dev environment, and changed the behavior of the function to something I can test for.
composer.json
{
...
"autoload-dev": {
"files": [
"tests/function-overrides.php"
],
...
],
...
}
tests/function-overrides.json
namespace Jacobemerick\Archangel;
function mail($to, $subject, $message, $headers) {
return compact('to', 'subject', 'message', 'headers');
}
tests/ArchangelTest.php
namespace Jacobemerick\Archangel;
class ArchangelTest
{
...
public function testSend()
{
$archangel = new Archangel();
$archangel->addTo('test@example.com');
$archangel->setSubject('Test Subject');
$archangel->setPlainMessage('Plain text message');
$response = $archangel->send();
$expectedResponse = array(
'to' => 'test@example.com',
'subject' => 'Test Subject',
'message' => 'Plain text message',
'headers' => 'X-Mailer: PHP/6.0.0',
);
$this->assertEquals($expectedResponse, $response);
}
With this set up, since I'm returning the result of the 'mail' function, I can create a namespaced mock of the function to return something that's predictable and represents the state of the parameters passed in. I've used this approach on several of my projects now, to test internal function usage and mocking the responses, and it seems to be working well enough.
My one fear with this approach is how easily my function-overrides.php file could completely destroy anything in production. If that file somehow got included during a production run than any internal function usage would be instantly overridden and, in this case, not a single mail would be sent. This is a side effect of using the same namespace for my tests and code, something I liked originally but am now having second thoughts about it. A simple solution would be to use a different namespace, though I'd have to re-engineer how my tests are structured.
Comments (0)