Permalink
Browse files

Spoof is born

  • Loading branch information...
0 parents commit a0e749f55f91d7399c77b4ea308da572d549ba82 @leihog committed Mar 21, 2012
Showing with 172 additions and 0 deletions.
  1. +17 −0 README.md
  2. +85 −0 exampleTest.php
  3. +41 −0 spoof.php
  4. +29 −0 spoofable.php
@@ -0,0 +1,17 @@
+
+>Testing classes with hard coded dependencies can be difficult.
+>Spoof tries to make that a little bit easier.
+
+Wether we like it or not singletons and hard coded dependencies are very
+common in PHP projects. When writing tests for such projects it's not
+uncommon that we run in to a case where we need an external resource, such as a
+database, because the code we want to test expects one to be present.
+
+If the code relies on dependency injection then we can use getMock() to emulate
+the resource, if not...
+well that's where **Spoof** comes in to the picture.
+
+Check out the example test in ExampleTest.php for an example of mocking a
+singleton whose implementation we are uninterested in during the test.
+
+[![Flattr this git repo](http://api.flattr.com/button/flattr-badge-large.png)](https://flattr.com/submit/auto?url=https://github.com/leihog/Spoof&title=%53%70%6f%6f%66)
@@ -0,0 +1,85 @@
+<?php
+
+include 'spoof.php';
+
+class Store
+{
+ protected static $instance;
+
+ public static function getInstance()
+ {
+ if ( !isset(self::$instance) )
+ {
+ self::$instance = new self();
+ }
+
+ return self::$instance;
+ }
+
+ public function set($key, $value)
+ {
+ // writing to external storage
+ echo "Original set called\n";
+ }
+
+ public function get($key)
+ {
+ // reading from external storage here
+ echo "Original get called\n";
+ }
+}
+
+class Foo
+{
+ public function getSomething()
+ {
+ return Store::getInstance()->get('foo');
+ }
+
+ public function doSomething($x)
+ {
+ $a = $this->getSomething();
+ Store::getInstance()->set('foo', ($a * $x));
+ }
+}
+
+class ExampleTests extends PHPUnit_Framework_TestCase
+{
+ public static function setUpBeforeClass()
+ {
+ set_new_overload( 'Spoof::loader' );
+ }
+
+ public static function tearDownAfterClass()
+ {
+ unset_new_overload();
+ }
+
+ public function testFoo()
+ {
+ Spoof::register( 'Store', array(
+ 'get' => 10,
+ ));
+
+ $f = new Foo();
+ $this->assertEquals( 10, $f->getSomething() );
+ }
+
+ public function testFooBar()
+ {
+ $store = array( 'foo' => 5 );
+ Spoof::instanceMethods(Store::getInstance(), array(
+ 'get' => function($key) use(&$store){
+ return $store[$key];
+ },
+ 'set' => function($key, $value) use(&$store){
+ $store[$key] = $value;
+ return null;
+ }
+ ));
+
+ $f = new Foo();
+ $f->doSomething(2);
+ $this->assertEquals( 10, $f->getSomething() );
+ }
+}
@@ -0,0 +1,41 @@
+<?php
+
+require_once 'spoofable.php';
+
+class Spoof
+{
+ public static $mocks = array();
+ public static $mockMethods = array();
+
+ public static function register( $class, $methods = array() )
+ {
+ //$mockClassName = md5($class . serialize($methods));
+ $mockClassName = "{$class}Mock";
+ if ( !class_exists($mockClassName, false) )
+ {
+ $classDefinition = "class {$mockClassName} extends Spoofable {}";
+ eval( $classDefinition );
+ }
+
+ self::$mocks[$class] = $mockClassName;
+ self::$mockMethods[$mockClassName] = $methods;
+ }
+
+ public static function instanceMethods($obj, $methods = array())
+ {
+ $rc = new ReflectionClass( get_class($obj) );
+ $rp = $rc->getProperty('methods');
+ $rp->setAccessible(true);
+ $rp->setValue($obj, $methods);
+ $rp->setAccessible(false);
+ }
+
+ public static function loader($class)
+ {
+ if ( isset(self::$mocks[$class]) )
+ {
+ return self::$mocks[$class];
+ }
+ return $class;
+ }
+}
@@ -0,0 +1,29 @@
+<?php
+
+abstract class Spoofable
+{
+ protected $methods;
+
+ public function __construct()
+ {
+ $className = get_class($this);
+ if ( isset(Spoof::$mockMethods[$className]) )
+ {
+ $this->methods = Spoof::$mockMethods[$className];
+ }
+ }
+
+ public function __call($method, $args)
+ {
+ if ( isset($this->methods[$method]) )
+ {
+ $return = $this->methods[$method];
+ if ( is_callable($return) )
+ {
+ return call_user_func_array($return, $args);
+ }
+ return $return;
+ }
+ return null;
+ }
+}

0 comments on commit a0e749f

Please sign in to comment.