"Unable to override class" error when trying to mock dependencies in PHPUnit

I am doing unit testing with PHPUnit on an existing codebase. I'm new to unit testing, but I understand that the goal is to completely isolate the code under test. This is tricky with my codebase because many classes depend on other classes in the codebase.

Dependencies are hardcoded into classes so there is no way to use dependency injection. I also don't want to refactor existing code just to test it. So to isolate each class from its dependencies, I created a library of "mock" classes (not using the PHPUnit mocking framework, but literally creating a library of classes that contain stub functions that return what I expect based on specific inputs).

The problem is, if during phpunit run I have a test that calls the mock class and then I try to test the actual class, I get a fatal error because PHP sees it as a reuse of the class. Here's a simplified example of what I mean. Note that this still fails even though I disabled all instances of the included classes and cleared the include path in the tearDown method. Again, I'm new to unit testing, so if I go wrong about this or miss something obvious, please let me know.

A bigger question might be if this approach will go a long way towards isolating the code, and if it's actually useful to use real objects as dependencies of my class.

#### real A

require_once 'b.class.php';

class A {   
    private $b;

    public function __construct() {
        $this->b = new B();
    }

    public function myMethod($foo) {
        return $this->b->exampleMethod($foo);
    }
}


#### real B

class B {
    public function exampleMethod($foo) {
        return $foo . "bar";
    }
}


#### mock B

class B {
    public function exampleMethod($foo) {
        switch($foo) {
            case 'test':
                return 'testbar';
            default:
                throw new Exception('Unexpected input for stub function ' . __FUNCTION__);
        }
    }
}


#### test A

class TestA extends PHPUnit_Extensions_Database_TestCase {
    protected function setUp() 
    {
        // include mocks specific to this test
        set_include_path(get_include_path() . PATH_SEPARATOR . 'tests/A/mocks');

        // require the class we are testing
        require_once 'a.class.php';     

        $this->a = new A();
    }

    public function testMyMethod() {
        $this->assertEquals('testbar', $a->myMethod('test'));
    }
}

#### test B

class TestB extends PHPUnit_Extensions_Database_TestCase {
    protected function setUp()
    {
        // include mocks specific to this test
        set_include_path(get_include_path() . PATH_SEPARATOR . 'tests/B/mocks');

        // require the class we are testing
        // THIS FAILS WITH: 'PHP Fatal error:  Cannot redeclare class B'
        require_once 'b.class.php';

        $this->b = new AB();
    }   
}

      

+3


source to share


4 answers


I think you need to run tests in isolated processes



Either you give an argument to execute your test: "--process-isolation" or you install $this->processIsolation = true;

+1


source


If for some reason (and there are some valid ones) you cannot use the PHPUnit mocking API (or Mockery), then you need to create the mock classes yourself.

The marked class must have the same "Type" as the real one (so the type of the hint continues) and for that reason you must extend the real one:



#### mock B

class Mock_B extends B {

      

This also works around the fact that you cannot have 2 classes with the same name in PHP :)

+1


source


You can also use namespaces when declaring mocks

0


source


You have two classes named "B". You cannot re-declare the class name (unless in different namespaces).

-1


source







All Articles