Testing abstract classes with PHPUnit

Today I got into the task of testing this code

// file AbstractFactory.php <?php namespace TAD\Helpers; abstract class AbstractFactory { public function create( $arg ) { $this->createInstance( $arg ); } abstract protected function createInstance( $arg ); } // file ControllersFactory.php <?php use jwage\SplClassLoader; use TAD\MVC\Helpers\FilePathResolver; use TAD\Helpers\EchoBot; namespace TAD\MVC\Factories; abstract class ControllerFactory extends \TAD\Helpers\AbstractFactory { protected $controllersFolderPath = null; protected $namespace = null; protected $controllerClassName = null; protected function createInstance( $arg ) { if ( $this->controllerExists( $arg ) ) { return $this->setAndReturn( $this->$controllerClassName ); } \TAD\Helpers\EchoBot::_output( "View controller for $arg has not been implemented yet." ); return null; } public function __construct() { // get the file the extending class was defined into $className = get_called_class(); $classInfo = new \ReflectionClass( $className ); $classFile = $classInfo->getFileName(); $filePathResolver = new \TAD\MVC\Helpers\FilePathResolver( $classFile ); // set the views folder $this->controllersFolderPath = $filePathResolver->getControllersFolderPath(); // get the extending class namespace $ns = $classInfo->get_namespace_name(); $this->namespace = $ns; // register the view components for autoloading $classLoader = new SplClassLoader( $ns, dirname( $classFile ) ); $classLoader->register(); } protected function controllerExists( $viewName ) { $controllerFilePath = $this->controllersFolderPath . '/' . $viewName . 'ViewController.php'; $controllerClassName = $this->namespace . '\\Controllers\\' . $viewName . 'ViewController'; if ( file_exists( $controllerFilePath ) ) { $this->$controllerClassName = $controllerClassName; return true; } return false; } abstract protected function setAndReturn( $controllerClassName ); } 

aside for the lack of any dependency injection possibility, it’s the factory injecting dependencies after all, writing tests for abstract classes in PHPUnit is possible but not that easy. Until now I always did it running my tests against a concrete class, a dummy one created for testing purposes, extending the abstract class I really wanted to test.

## Can I test an abstract class without extending it?
Yes because I’m actually extending it mocking it.
No because I’m not really testing the class but another class extending it.

## Practical tests
I wanted to test that calling the create method on a class extending the ControllersFactory class would have triggered the chain reaction resulting in a call to ControllersFactory::createInstance method.
To make things clear the chain of extensions and method calling should be

ControllersFactoryExtendingClass::create() AbstractFactory::create() ControllersFactory::createInstance() 

The test function I used to accomplish that is

public function testCreateWillCallInitAndCreateInstance() { $sut = $this->getMockForAbstractClass( // I have to use a mock generator made for abstract classes '\TAD\MVC\Factories\ControllerFactory', // the fully qualified name of the abstract class to mock array(), // no parameters for the class constructor '', // no new name for the class to mock FALSE, // do not call the original constructor TRUE, // call original clone TRUE, // call autoload array( 'createInstance' ) ); // I will mock the createInstance method $sut->expects( $this->once() )->method( 'createInstance' )->will( $this->returnValue( TRUE ) ); $sut->create( 'SomeView' ); } 

Another expectation I have is that, no matter how a class extending the ControllerFactory implements the setAndReturn method, calling create with a non-existing parameter should output something polite that can be printed on a site without making the script come to a screeching halt.

public function testCreateNonExistingControllerEchoesToScreen() { $stub = $this->getMockForAbstractClass( '\TAD\MVC\Factories\ControllerFactory', array(), '', FALSE, TRUE, TRUE, array( 'controllerExists' ) ); $stub->expects( $this->once() )->method( 'controllerExists' )->will( $this->returnValue( FALSE ) ); $this->expectOutputRegex( "/.*SomeView.*/" ); $stub->create( 'SomeView' ); } 

The abstract classes are just classes at the end of the day

Simply using the getMockForAbstractClass method will make partially or totally abstract classes testable generating, de facto, an extending class with its methods stubbed to null-returning ones. The syntax is a bit lengthy but not so different from the getMock one.