Blog Archives
The reason why singleton is a “problem” with PHPUnit
Singleton is a common design pattern. It’s easy to understand and we can easily implement it with PHP:
<?php
class Foo
{
static private $instance = NULL;
static public function getInstance()
{
if (self::$instance == NULL) {
self::$instance = new static();
}
return self::$instance;
}
}
Maybe this pattern is not as useful as it is in J2EE world. With PHP everything dies within each request, so we cannot persist our instances between requests (without any persistent mechanism such as databases, memcached or external servers). But at least in PHP we can share the same instance, with this pattern, in our script. No matter were we are, we use Foo::getInstance() and we get our instance. It useful when we work, for example, with database connections and service containers.
If we work with TDD we most probably use PHPUnit. Imagine a simple script to test the instantiation of our Foo class
class FooTest extends PHPUnit_Framework_TestCase
{
public function testGetInstance()
{
$this->assertInstanceOf('Foo', Foo::getInstance());
}
}
All is green. It works.
Imagine now we want to “improve” our Foo class and we want to create a counter:
public function testCounter()
{
$foo = Foo::getInstance();
$this->assertEquals(0, $foo->getCounter());
$foo->incrementCounter();
$this->assertEquals(1, $foo->getCounter());
}
Now our test fails, So we change our implementation of the class to pass the test and we add to Foo class
private $counter = 0;
public function getCounter()
{
return $this->counter;
}
public function incrementCounter()
{
$this->counter++;
}
It’s green again. We are using a singleton pattern to get the instance of the class and we are using TDD, so what’s the problem? I will show you with an example. Imagine we need to improve our Foo class and we want a getter to say if counter is even or not. We add a new test:
public function testIsOdd()
{
$foo = Foo::getInstance();
$this->assertEquals(FALSE, $foo->counterIsOdd(), '0 is even');
$foo->incrementCounter();
$this->assertEquals(TRUE, $foo->counterIsOdd(), '1 is uneven');
}
And we implement the code to pass the test again:
public function counterIsOdd()
{
return $this->counter % 2 == 0 ? FALSE : TRUE;
}
But it doesn’t work. Our test is red now. Why? The answer is simple. We are sharing the same instance of Foo class in both tests, so in the first one our counter start with 0 (the default value) but with the second one it start with 1, because the first test has changed the state of the instance (and we are reusing it).
This is a trivial example, but real world problems with this issue are a bit nightmare. Imagine, for example, that we use singleton pattern to get the database connection (a common usage of this pattern). Suddenly we break one test (because we have created a wrong SQL statement). Imagine we have a test suite with 100 assertions. We have broken one assertion (the second one, for example), and our PHPUnit script will say that we have 99 errors (the second one and all the following ones). That happens because our script can’t reuse the database connection (it’s broken in the second test). Too much garbage in the output of the PHPUnit script and that’s not agile.
How to solve it? We have two possible solutions. The first one is avoid singletons as a plague. We have alternatives such as dependency injection, but if we really want to use them (they are not forbidden, indeed) we need to remember to write one static function in our class to reset all our static states and call after each test. PHPUnit allow to execute something at the end of each test with tearDown() function. So we add to our test:
class FooTest extends PHPUnit_Framework_TestCase
{
...
public function tearDown()
{
Foo::tearDown();
}
}
And now we implement the tearDown function in our Foo Class
public static function tearDown()
{
static::$instance = NULL;
}
And all works again. All is green and green is cool, isn’t it?
Note: Take into account that “problem” is between quotes in the title. That’s beacause singleton is not a “real” problem. We can use it, it isn’t forbiden, but we need to realize the collateral effects that its usage can throw to our code.
Runtime Classes. A experiment with PHP and Object Oriented Programming
Last week I was thinking about creation of a new type of classes. PHP classes but created dynamically at run time. When this idea was running through my head I read the following article and I wanted to write something similar. Warning: Probably that it is something totally useless, but I wanted to create a working prototype (and it was fun to do it
). Let’s start:
We are going to crate something like this:
class Human
{
private $name;
function __construct($name)
{
$this->name = $name;
}
function hello()
{
return "{$this->name} says hello";
}
}
$gonzalo = new Human('Gonzalo');
$peter = new Human('Peter');
echo $gonzalo->hello(); // outputs: Gonzalo says hello
echo $peter->hello(); // outputs: Peter says hello
but without using class statement, adding the constructor and hello method dinamically.
function testSimpleUsage()
{
$human = HClass::define()
->fct(HClass::__construct, function($self, $name) {$self->name = $name;})
->fct('hello', function($self) {
return "{$self->name} says hello";
});
$gonzalo = $human->create('Gonzalo');
$peter = $human->create('Peter');
$this->assertEquals("Gonzalo says hello", $gonzalo->hello());
$this->assertEquals("Peter says hello", $peter->hello());
}
I also want to test undefinded functions with an exception:
function testCallingUndefinedFunctions()
{
$human = HClass::define()
->fct(HClass::__construct, function($self, $name) {$self->name = $name;})
->fct('hello', function($self) {
return "{$self->name} says hello";
});
$gonzalo = $human->create('Gonzalo');
$this->setExpectedException('Exception', "ERROR Method 'goodbye' does not exits");
$gonzalo->goodbye();
}
And simple inheritance too
function testInheritance()
{
$human = HClass::define()
->fct(HClass::__construct, function($self, $name) {$self->name = $name;})
->fct('hello', function($self) {
return "{$self->name} says hello";
});
$shyHuman = HClass::define($human)
->fct('hello', function($self) {
return "{$self->name} is shy and don't says hello";
});
$gonzalo = $human->create('Gonzalo');
$peter = $shyHuman->create('Peter');
$this->assertEquals("Gonzalo says hello", $gonzalo->hello());
$this->assertEquals("Peter is shy and don't says hello", $peter->hello());
}
Now we are going to create dinamically functions:
function testDinamicallyFunctionCreation()
{
$human = HClass::define()
->fct(HClass::__construct, function($self, $name) {$self->name = $name;})
->fct('hello', function($self) {
return "{$self->name} says hello";
});
$gonzalo = $human->create('Gonzalo');
$this->assertEquals("Gonzalo says hello", $gonzalo->hello());
try {
$gonzalo->goodbye();
} catch (Exception $e) {
$this->assertEquals("ERROR Method 'goodbye' does not exits", $e->getMessage());
}
$human->fct('goodbye', function($self) {
return "{$self->name} says goodbye";
});
$this->assertEquals("Gonzalo says goodbye", $gonzalo->goodbye());
}
And that’s it. It works. probably with PHP5.4 we can drop the “$self” variable. It’s an ugly trick to pass the real instance of the class to the callback, thanks to the “Added closure $this support back” feature added in the new version of PHP. But at least now we need to it.
And now another turn the screw. Let’s try to create the FizzBuzz kata with those “Runtime Classes”. I will create two versions of FizzBuzz. Here you can see my implementation of both versions with “standard” PHP. Whith a simple class, and another one with Two classes and Dependency Injection. Now using the experiment:
function testFizzBuzz()
{
$fizzBuzz = HClass::define()
->fct('run', function($self, $elems = 100) {
list($fizz, $buzz) = array('Fizz', 'Buzz');
return array_map(function ($element) use ($fizz, $buzz) {
$out = array();
if ($element % 3 == 0 || strpos((string) $element, '3') !== false ) {
$out[] = $fizz;
}
if ($element % 5 == 0 || strpos((string) $element, '5') !== false ) {
$out[] = $buzz;
}
return (count($out) > 0) ? implode('', $out) : $element;
}, range(0, $elems-1));
});
$fizzBuzz = $fizzBuzz->create();
$arr = $fizzBuzz->run();
$this->assertEquals(count($arr), 100);
$this->assertEquals($arr[1], 1);
$this->assertEquals($arr[3], 'Fizz');
$this->assertEquals($arr[4], 4);
$this->assertEquals($arr[5], 'Buzz');
$this->assertEquals($arr[6], 'Fizz');
$this->assertEquals($arr[20], 'Buzz');
$this->assertEquals($arr[13], 'Fizz');
$this->assertEquals($arr[15], 'FizzBuzz');
$this->assertEquals($arr[53], 'FizzBuzz');
}
function testAnotherFizzBuzzImplementationWithDependencyInjection()
{
$fizzBuzz = HClass::define();
$fizzBuzz->fct(HClass::__construct, function($self, $fizzBuzzElement) {
$self->fizzBuzzElement = $fizzBuzzElement;
});
$fizzBuzz->fct('run', function($self, $elems = 100) {
$out = array();
foreach (range(1, $elems) as $elem) {
$out[$elem] = $self->fizzBuzzElement->render($elem);
}
return $out;
});
$fizzBuzzElement = HClass::define()
->fct('render', function($self, $element) {
list($fizz, $buzz) = array('Fizz', 'Buzz');
$out = array();
if ($element % 3 == 0 || strpos((string) $element, '3') !== false ) {
$out[] = $fizz;
}
if ($element % 5 == 0 || strpos((string) $element, '5') !== false ) {
$out[] = $buzz;
}
return (count($out) > 0) ? implode('', $out) : $element;
});
$fbe = $fizzBuzzElement->create();
$this->assertEquals($fbe->render(1), 1);
$this->assertEquals($fbe->render(2), 2);
$this->assertEquals($fbe->render(3), 'Fizz');
$this->assertEquals($fbe->render(4), 4);
$this->assertEquals($fbe->render(5), 'Buzz');
$this->assertEquals($fbe->render(6), 'Fizz');
$this->assertEquals($fbe->render(20), 'Buzz');
$this->assertEquals($fbe->render(13), 'Fizz');
$this->assertEquals($fbe->render(15), 'FizzBuzz');
$this->assertEquals($fbe->render(53), 'FizzBuzz');
$fb = $fizzBuzz->create($fbe);
$arr = $fb->run();
$this->assertEquals(count($arr), 100);
$this->assertEquals($arr[1], 1);
$this->assertEquals($arr[3], 'Fizz');
$this->assertEquals($arr[4], 4);
$this->assertEquals($arr[5], 'Buzz');
$this->assertEquals($arr[6], 'Fizz');
$this->assertEquals($arr[20], 'Buzz');
$this->assertEquals($arr[13], 'Fizz');
$this->assertEquals($arr[15], 'FizzBuzz');
$this->assertEquals($arr[53], 'FizzBuzz');
}
And that’s all. As I said before probably this “Hybrid Classes” or “Runtime Classes” (I don’t know how to name them) are totally useless, but it’s fun to do it.
phpunit HclassTest.php PHPUnit 3.4.5 by Sebastian Bergmann. ...... Time: 0 seconds, Memory: 5.00Mb OK (6 tests, 39 assertions)
Full code on github























