鉴于此课程:
class MyBuilder {
public function build($param1, $param2) {
// build dependencies ...
return new MyClass($dep1, $dep2, $dep3);
}
}
我如何对这个班级进行单元测试?
单元测试意味着我想测试它的行为,所以我想测试它用正确的依赖项构建我的对象。但是,new
指令是硬编码的,我不能嘲笑它。
现在,我已经将类的名称添加为参数(因此我可以提供模拟类的类名),但它很难看:
class MyBuilder {
public function build($classname, $param1, $param2) {
// build dependencies ...
return new $classname($dep1, $dep2, $dep3);
}
}
是否有清洁的解决方案或设计模式使我的工厂可测试?
答案 0 :(得分:3)
工厂本质上是可测试的,你只是试图对实现过于严格控制。
您将检查是否通过$this->assertInstanceOf()
获得了课程的实例。然后使用生成的对象,确保正确设置属性。为此,您可以使用任何公共访问器方法或使用PHPUnit中可用的$this->assertAttribute*
方法。
许多常见断言还能够检查受保护属性和私有属性的属性。
我不会在参数列表中指定类名,因为您的用法是工厂只返回一种类型,而且只是更改的依赖项。使其返回模拟对象类型是不必要的,并使您的测试更复杂。
测试最终看起来像这样:
public function testBuild() {
$factory = new MyBuilder();
//I would likely put the following into a data provider
$param1 = 'foo';
$param2 = 'bar';
$depen1 = 'boo';
$depen2 = 'baz';
$depen3 = 'boz';
$object = $factory->build($param1, $param2);
$this->assertInstanceOf('MyClass', $object);
//Check the object definition
//This would change depending on your actual implementation of your class
$this->assertAttributeEquals($depen1, 'attr1', $object);
$this->assertAttributeEquals($depen2, 'attr2', $object);
$this->assertAttributeEquals($depen3, 'attr3', $object);
}
您现在确保工厂返回正确的对象。首先要确保它是正确的类型。然后确保它已正确初始化。
你依靠MyClass
的存在来通过测试,但这不是一件坏事。您的工厂旨在创建MyClass
个对象,因此如果该类未定义,那么您的测试肯定会失败。
在开发过程中进行测试失败也不是一件坏事。
答案 1 :(得分:1)
那么你想测试什么?
所以我想测试它使用正确的依赖项构建我的对象。
我确实看到了这个问题。你有可能创建一个具有不正确依赖关系的对象(首先不应该是这种情况,或者在其他测试中测试,而不是在工厂中测试),或者你想要测试你不应该测试的工厂细节所有
否则 - 如果它不是在嘲笑你正在寻找的工厂 - 我认为没有理由为什么简单
$actual = $subject->build($param1, $param2);
$this->assertInstanceOf('MyClass', $actual);
不会成功。它测试工厂构建方法的行为,它返回正确的类型。
对于测试,您可以创建从您的Builder扩展的MockBuilder:
class MyMockBuilder extends MyBuilder {
public function build($param1, $param2) {
// build dependencies ...
return new MyMockClass($dep1, $dep2, $dep3);
}
}
让classname成为参数1:1似乎对我来说不实用,因为它将工厂变成了不同的东西。创造是工厂的细节,没有你外化。所以它应该被封装。因此MockBuilder用于测试。你切换工厂。
答案 2 :(得分:0)
在我看来,你需要为该构建器验证两件事:
检查实例很容易。验证值需要一些技巧。
执行此操作的简单方法是更改自动加载器。您需要确保在请求自动加载器MyClass
时,而不是/src/app/myclass.php
文件加载/test/app/myclass.php
,它实际上包含一个“透明”模拟(您可以使用简单的getter验证值。)坏主意
<强>更新强>
此外,如果您不想使用自动加载器,您可以在myBuilderTest.php
文件的顶部包含模拟类文件,其中包含MyClass
的定义。
......这实际上似乎是一种更清洁的方式。
namespace Foo\Bar;
use PHPUnit_Framework_TestCase;
require TEST_ROOT . '/mocks/myclass.php'
class MyBuilderTest extends PHPUnit_Framework_TestCase
{
public function MyBuilder_verify_injected_params_test()
{
$target = new MyBuilder;
$instance = $target->build('a', 'b');
$this->assertEquals('a', $instance->getFirstConstructorParam();
}
}