2011-09-05 56 views
2

我正在重组遗留php项目的测试用例来测试套件,并遇到测试依赖于另一个类的类的问题。这是测试文件夹的原始目录布局。重新组织测试文件的选项,其中一个类依赖于另一个类?

tests/ 
    | 
    +- models/ 
    |  | ClassATest.php 
    |  | ClassBTest.php 
    |  | ... 
    +- controllers/ 
    |  | ... 

目前,没有测试套件。每个测试都是分开运行的。

为了重新组织这些测试,我创建了phpunit.xml并将测试分组到如下的套件中。

phpunit.xml

<phpunit bootstrap="..."> 
    <testsuite name="Models"> 
     <directory>./models</directory> 
    </testsuite> 
    ... 
</phpunit> 

现在我惹上麻烦。我们有两个模型,ClassA和ClassB,ClassB的方法依赖于ClassA。

class ClassA 
{ 
    public function getValue(...) 
    { 
     ... 
    } 
} 

class ClassB 
{ 
    public function doSomething() 
    { 
     $a = new ClassA(); 
     $x = $a->getValue(...); 
     // do something with $x 
     ... 
    } 
} 

在这里,我想我们都会在测试时看到问题。模拟ClassA :: getValue(),ClassBTest.php已经(重新)定义ClassA,如下所示。

ClassBTest.php

class ClassA 
{ 
    public function getValue(...) 
    { 
     return 'a mock value'; 
    } 
} 

class ClassBTest extends PHPUnit_Framework_TestCase 
{ 
    public function testDoSomething() 
    { 
     $b = new ClassB(); 
     // Make an assertion with $b->doSomething() 
    } 
} 

所以,当我们分别测试每一个模型,它的工作原理。但是,当在套件中运行时,它会导致错误,因为现在ClassA在运行过程的某个点重新声明,并且phpunit被终止。

我的问题是如何处理这种情况。我尝试了一些选择。

  • 删除测试文件中的重新定义的类并使用依赖注入。这种方式并不是首选,因为我们不想修改用于依赖注入的模型类。
  • 使用组注释来排除这样的测试文件,如ClassBTest.php并单独运行排除的文件。这个选项不方便。

我们可以配置phpunit在一个进程中运行一组文件,在另一个进程中运行另一组文件吗? PHP可以在运行时动态加载和卸载类吗?如果您有其他选择,请提出建议。

谢谢!

回答

2

PHP可以在运行时动态加载和卸载类吗?如果您有其他选择,请提出建议。

是的。这是测试您无法重构的代码的最后手段。看看the runkit extension, method_rename。你可以重命名你想“模拟”的orignal方法。创建你的模拟函数,并在测试后重新命名它。

较新的php test helpers也提供重命名功能。


或者,您可以使用"runInIsolation"/"process-isolation"。每个测试用例都将在另一个PHP进程中运行。如果你只用测试用例来定义这些类,那么你将不会遇到“已定义”的问题。


,不依赖于扩展或spawing很多PHP程序的,使您的测试套件慢

这似乎有点hackisch只是提供所有的选择第三个选项:

class ClassB 
{ 
    public function doSomething() 
    { 
     $a = new ClassA(); 
     $x = $a->getValue(...); 
     // do something with $x 
     ... 
    } 
} 

没有重构你可以到以下几点:

class ClassB { public function doSomething() { $ a = $ this-> getClassA(); $ x = $ a-> getValue(...); //使用$ X 东西... }

protected function getClassA() { 
    if(!$this->classA) { 
     return new ClassA(); 
    } 
    return $this->classA; 
} 

public function setClassAForTesting(ClassA $classA) { 
    $this->classA = $classA; 
} 

}

杂波您的代码库 “为测试代码”,但使用setter injection允许您使用依赖注入测试时您的生产代码将像以前一样工作。

这种方法需要一些工作,但根据我的经验,它比使用PHP扩展攻击行为更可持续。

+0

谢谢,edorian!与团队讨论使用php扩展和重构模型。可能我们会选择第一个选项。如果phpunit允许我们在单独的进程中运行每组文件,会不会很好?我们是否应该向Sebastian Bergmann提出特征请求;) – peidiam

相关问题