'不能重新声明课程'尝试模拟PHPUnit中的依赖项时出错

时间:2012-03-23 23:58:33

标签: php unit-testing testing mocking phpunit

我正在现有代码库上使用PHPUnit实现单元测试。我对单元测试很新,但我知道目标是完全隔离正在测试的代码。这对我的代码库来说很难实现,因为许多类依赖于代码库中的其他类。

依赖项被硬编码到类中,因此无法使用依赖项注入。我也不想仅仅为了测试它而重构现有代码。 因此,为了将每个类与它的依赖关系隔离开来,我创建了一个“模拟”类库(不是通过使用PHPUnit的模拟框架,而是通过字面创建一个包含存根函数的类库,这些函数返回我期望的特定类输入)。

问题是,如果在phpunit运行期间,我有一个调用mock类的测试,然后我尝试测试实际的类,我得到一个致命错误,因为PHP认为这是重新声明类。这是我的意思的简化示例。请注意,即使我取消设置我所包含的类的所有实例并清除tearDown方法中的包含路径,这仍然会失败。再一次,我是单位测试的新手,所以如果我以错误的方式解决这个问题或遗漏一些明显的东西,请告诉我。

一个更大的问题可能是,如果这种方法在隔离代码的方向上走得很远,并且如果使用真实对象作为我的类的依赖性实际上是一个好处。

#### 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 个答案:

答案 0 :(得分:1)

我认为你需要在孤立的进程中运行你的测试

要么为执行测试提出参数:“ - process-isolation”或者设置$this->processIsolation = true;

答案 1 :(得分:1)

如果由于某种原因(并且有一些有效的)无法使用PHPUnit模拟API(或Mockery),那么您需要创建自己的模拟类。

模拟类应该具有与真实类相同的“类型”(因此类型提示仍然有效),因此您应该扩展真实类:

#### mock B

class Mock_B extends B {

这也解决了这样一个事实,即PHP中不能有2个具有相同名称的类:)

答案 2 :(得分:0)

您还可以在声明模拟时使用名称空间