用存根替换所有 class 个实例
Replace all class instances with stub
我正在测试一个 class,我们称它为 ClassUnderTest
,使用另一个 class,我们称它为 OtherClass
。在我的测试中我这样做:
$OtherClassStub = $this->createStub(OtherClass::class);
$OtherClassStub->method(...)
->willReturn(...);
$ClassUnderTest->otherClass = $OtherClassStub;
行得通。但是当 $ClassUnderTest
调用 new OtherClass()
时,会创建原始的 OtherClass
class 而不是存根。
如何实现测试上下文中 OtherClass
的每个可能实例都被存根替换?
Perhaps 此处提供了您的问题的解决方案:
How to Build a PHP Plugin Module System
这是一种将 classes 作为插件加载的方法,它们可以相互调用。通过 mod 稍微改进这个系统,您可以根据自己的代码创建任意数量的“new OtherClass()”,并且仍然可以访问其他 classes 的所有内容。如果你想要一个 class 的多个实例,perhaps mod将它变成这个方向:
function load ($module,$instance) {
if (isset($this->$module->$instance)) { return true; }
从上面link:
<?php
class Core {
// (A) PROPERTIES
public $error = ""; // LAST ERROR MESSAGE
public $pdo = null; // DATABASE CONNECTION
public $stmt = null; // SQL STATEMENT
public $lastID = null; // LAST INSERT/UPDATE ID
// (B) LOAD SPECIFIED MODULE
// $module : module to load
function load ($module) {
// (B1) CHECK IF MODULE IS ALREADY LOADED
if (isset($this->$module)) { return true; }
// (B2) EXTEND MODULE ON CORE OBJECT
$file = PATH_LIB . "LIB-$module.php";
if (file_exists($file)) {
require $file;
$this->$module = new $module();
// EVIL POINTER - ALLOW OBJECTS TO ACCESS EACH OTHER
$this->$module->core =& $this;
$this->$module->error =& $this->error;
$this->$module->pdo =& $this->pdo;
$this->$module->stmt =& $this->stmt;
return true;
} else {
$this->error = "$file not found!";
return false;
}
}
}
ps。感谢 mod,是他让我付出了更多努力,让这个答案在线。现在答案好多了。
根据你的描述我推断原则上你有这样的事情:
class OtherClass {
protected function someMethod(): bool
{
// determine $x ...
return $x;
}
}
class ClassUnderTest {
public OtherClass $otherClass;
public function methodToBeTested(): bool
{
$otherClass = new OtherClass();
return $otherClass->someMethod();
}
}
class ClassUnderTestTest extends TestCase {
public function testMethodToBeTested(): void
{
$otherClassStub = $this->createStub(OtherClass::class);
$otherClassStub->method('someMethod')
->willReturn(true);
$classUnderTest = new ClassUnderTest();
$classUnderTest->otherClass = $otherClassStub;
$result = $classUnderTest->methodToBeTested();
$this->assertTrue($result);
}
}
现在您测试中的断言可能成立,也可能失败。为什么?因为您没有调用在 $otherClassStub
上存根的方法。相反,你在你正在测试的方法中实例化一个新的 $otherClass
对象(或某处)。
您的 ClassUnderTest
应该始终使用 ClassUndertTest::otherClass
属性中的 OtherClass
对象(假设这就是您首先将其放在那里的原因)。
或者您可以使用其他形式的依赖注入,例如通过使用像 Symfony 或 Laravel 这样的框架。 (在 Symfony 的情况下,你甚至可以使用 only 和 DependencyInjection Component,不知道 Laravel 是否也可以。)
您实际问题的简单答案是:您无法更改 new
关键字的行为。在 class will always instantiate a new object based on exactly that class 上调用 new
,除非 class 的构造函数定义了其他内容。
(您可能想直接了解 classes 和对象的概念,您的代码示例以及您的问题似乎表明您对此不太清楚。也许阅读一下以及 concept of dependency injection 上的内容会对您有所帮助。)
我正在测试一个 class,我们称它为 ClassUnderTest
,使用另一个 class,我们称它为 OtherClass
。在我的测试中我这样做:
$OtherClassStub = $this->createStub(OtherClass::class);
$OtherClassStub->method(...)
->willReturn(...);
$ClassUnderTest->otherClass = $OtherClassStub;
行得通。但是当 $ClassUnderTest
调用 new OtherClass()
时,会创建原始的 OtherClass
class 而不是存根。
如何实现测试上下文中 OtherClass
的每个可能实例都被存根替换?
Perhaps 此处提供了您的问题的解决方案: How to Build a PHP Plugin Module System
这是一种将 classes 作为插件加载的方法,它们可以相互调用。通过 mod 稍微改进这个系统,您可以根据自己的代码创建任意数量的“new OtherClass()”,并且仍然可以访问其他 classes 的所有内容。如果你想要一个 class 的多个实例,perhaps mod将它变成这个方向:
function load ($module,$instance) {
if (isset($this->$module->$instance)) { return true; }
从上面link:
<?php
class Core {
// (A) PROPERTIES
public $error = ""; // LAST ERROR MESSAGE
public $pdo = null; // DATABASE CONNECTION
public $stmt = null; // SQL STATEMENT
public $lastID = null; // LAST INSERT/UPDATE ID
// (B) LOAD SPECIFIED MODULE
// $module : module to load
function load ($module) {
// (B1) CHECK IF MODULE IS ALREADY LOADED
if (isset($this->$module)) { return true; }
// (B2) EXTEND MODULE ON CORE OBJECT
$file = PATH_LIB . "LIB-$module.php";
if (file_exists($file)) {
require $file;
$this->$module = new $module();
// EVIL POINTER - ALLOW OBJECTS TO ACCESS EACH OTHER
$this->$module->core =& $this;
$this->$module->error =& $this->error;
$this->$module->pdo =& $this->pdo;
$this->$module->stmt =& $this->stmt;
return true;
} else {
$this->error = "$file not found!";
return false;
}
}
}
ps。感谢 mod,是他让我付出了更多努力,让这个答案在线。现在答案好多了。
根据你的描述我推断原则上你有这样的事情:
class OtherClass {
protected function someMethod(): bool
{
// determine $x ...
return $x;
}
}
class ClassUnderTest {
public OtherClass $otherClass;
public function methodToBeTested(): bool
{
$otherClass = new OtherClass();
return $otherClass->someMethod();
}
}
class ClassUnderTestTest extends TestCase {
public function testMethodToBeTested(): void
{
$otherClassStub = $this->createStub(OtherClass::class);
$otherClassStub->method('someMethod')
->willReturn(true);
$classUnderTest = new ClassUnderTest();
$classUnderTest->otherClass = $otherClassStub;
$result = $classUnderTest->methodToBeTested();
$this->assertTrue($result);
}
}
现在您测试中的断言可能成立,也可能失败。为什么?因为您没有调用在 $otherClassStub
上存根的方法。相反,你在你正在测试的方法中实例化一个新的 $otherClass
对象(或某处)。
您的 ClassUnderTest
应该始终使用 ClassUndertTest::otherClass
属性中的 OtherClass
对象(假设这就是您首先将其放在那里的原因)。
或者您可以使用其他形式的依赖注入,例如通过使用像 Symfony 或 Laravel 这样的框架。 (在 Symfony 的情况下,你甚至可以使用 only 和 DependencyInjection Component,不知道 Laravel 是否也可以。)
您实际问题的简单答案是:您无法更改 new
关键字的行为。在 class will always instantiate a new object based on exactly that class 上调用 new
,除非 class 的构造函数定义了其他内容。
(您可能想直接了解 classes 和对象的概念,您的代码示例以及您的问题似乎表明您对此不太清楚。也许阅读一下以及 concept of dependency injection 上的内容会对您有所帮助。)