我正在重新组织遗留php项目的测试用例来测试套件并遇到测试依赖于另一个类的类的问题。以下是tests文件夹的原始目录布局。
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被终止。
我的问题是如何处理这种情况。我尝试了一些选择。
我们可以配置phpunit来运行进程中的一组文件和另一个进程中的另一组文件吗? PHP可以在运行时动态加载和卸载类吗?如果您有任何其他选择,请建议。
谢谢!
答案 0 :(得分:2)
PHP可以在运行时动态加载和卸载一个类吗?如果您有任何其他选择,请建议。
是。这是测试无法重构的代码的最后手段。看看the runkit extension, method_rename。您可以重命名要“模拟”的orignal方法。创建模拟函数并在测试后重命名。
较新的php test helpers
还提供了重命名功能。
或者您可以使用"runInIsolation" / "process-isolation"。每个测试用例都将在另一个PHP进程中运行。如果您只使用测试用例定义这些类,则不会遇到“已定义”的问题。
第三个选项不依赖于扩展或产生大量使您的测试套件变慢的php进程
看起来有点像hackisch但只是提供所有选项:
class ClassB
{
public function doSomething()
{
$a = new ClassA();
$x = $a->getValue(...);
// do something with $x
...
}
}
没有多重构您可以进行以下操作:
类ClassB { 公共功能doSomething() { $ a = $ this-&gt; getClassA(); $ x = $ a-&gt; 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 Extensions破解行为更具可持续性。