I have long wanted to share with the public a way to test code that uses functions for working with the external environment: sockets, databases, files, and anything else. Today, after seeing the article
Runkit + PHPUnit = 100% test coverage , I decided that now is the time.
The solution with Runkit is beautiful, but there is one problem - Runkit does not spread along with PHP, it must be installed separately. I want to suggest an approach that works in the usual delivery of PHP 5.3+, on one condition - the project must use namespaces.
Idea
The idea is very simple and is based on
the name resolution rules in PHP : the called function is first searched in the current namespace, and only then the built-in PHP function is called. For example, in the following piece of code:
')
namespace A;
foo();
PHP will first try to call A \ foo () and only then foo (). So, if we declare a function with the name, say, “fsockopen” in the namespace of the project, then it will be the first to be called.
Application
Suppose we have this class:
src / MyClass.php<?php
namespace MyNS;
class MyClass
{
public function someMethod()
{
$fp = fsockopen( 'example.org' , 123);
}
}
* This source code was highlighted with Source Code Highlighter .
To override the fsockopen function, create the following file in the tests:
tests / helpers.php<?php
namespace MyNS;
function fsockopen($hostname, $port)
{
$GLOBALS[ 'fsockopen' ] = array($hostname, $port);
}
* This source code was highlighted with Source Code Highlighter .
Now you can write this test:
tests / MyClass_Test.php<?php
namespace MyNS\Tests;
use PHPUnit_Framework_TestCase;
use MyNS\MyClass;
require 'helpers.php' ;
require '../src/MyClass.php' ;
class MyClass_Test extends PHPUnit_Framework_TestCase
{
public function test_someMethod()
{
$test = new MyClass();
$test->someMethod();
$ this ->assertEquals(array( 'example.org' , 123), $GLOBALS[ 'fsockopen' ]);
}
}
* This source code was highlighted with Source Code Highlighter .
In this test, not the built-in fsockopen will be called, but declared in helpers.php.
Of course, the $ GLOBALS option is not the only one possible, and is shown here only for simplicity. What I propose is not a ready-made solution, but only an approach that everyone can adapt to their needs.