测试工厂方法
Test factory method
编写测试以覆盖 100% 的代码是我们应该尝试实现的目标。但是我想出了我不知道如何测试方法(工厂方法)的情况:
public function getDocument(){
$document = new Document();
$document->settings(new Settings());
$document->filesystem(new Filesystem('e:'));
return $document;
}
该方法的目的是创建文档的快捷方式,而不是每次都写3行。
如何测试这个方法?
或者这就是为什么我们有 @codeCoverageIgnoreStart 块的情况?正是出于这个原因,PHPUnit 提供了这种注解。
编辑:
这种方法背后的主要思想是让客户的生活更轻松。仅此而已,没有配置等(但该方法将是执行此操作的好地方)。
//I don't want bother client with Settings() and Filesystem('e:')
$document = new Document(new Settings(), new Filesystem()); //NO
$document = Files.getDocument() //much easier and shorter.
//Changing API to getDocument($var, $var) make no sense, the same thing I could have normally.
$document = new Document(new Settings(),new Filesystem('e:'));
也许我应该考虑一下我是否真的应该提供那个方法,想要使用文档的用户应该知道依赖关系,它不应该被隐藏。
Inject your dependencies (Document
, Settings
, Filesystem
) via the constructor, then use test doubles视情况而定。
也重新考虑一下你的100%覆盖率政策,绝对不清楚这实际上是一件好事。
我找到了答案:此代码不可测试。
Wherever you have new XXX(...) in a method under test, you are doomed.
更多:
将依赖传递给工厂方法,在里面初始化新对象,并适当配置。在测试中,依赖项将是模拟的而不是真实的对象。
方法一
允许创建依赖项的传递工厂:
public function getDocument(SettingsFactory $sf, FilesystemFactory $ff){
$document = new Document();
$document->settings($sf->getSettings());
$document->filesystem($ff->getFilesystem());
return $document;
}
在测试中,你应该:
- 创建
Settings
实例或 mock 和一个 SettingsFactory
mock 期望一次调用 getSettings
并将 return Settings
实例
- 创建
Filesystem
实例或 mock 和一个 FilesytemFactory
mock 期望一次调用 getFilesystem
并将 return Filesystem
实例
- 调用
DocumentFactory
方法,传递工厂。检查 Document
对象是否 returned
- 检查分配给
Document
的对象是否与您为 return 配置的模拟相同
A 变体 将 getSettings
和 getFilesystem
作为 Document
工厂的方法。在那种情况下,您应该创建工厂的部分模拟,并对其设置期望。所以真正的getDocument
方法被调用了,但是当getSettings
和getFilesystem
方法被调用时,你return控制实例。
方法二
传递实际依赖项:
public function getDocument(Settings $settings, Filesystem $filesystem) {
$document = new Document();
$document->settings($settings);
$document->filesystem($filesystem);
return $document;
}
在测试中,你应该:
- 创建
Settings
实例或模拟
- 创建
Filesystem
实例或模拟
- 调用
DocumentFactory
方法,传递Settings
和Filesystem
。检查 Document
对象是否 returned
- 检查分配给
Document
的对象是否与您传递给工厂方法的实例相同
这个方法有什么作用? Returns 已初始化 Document
对象。因此,您只需验证返回值是一个 Document
实例,并且它已设置 Settings
和 Filesystem
对象。如果你有这些吸气剂就很容易,否则你必须访问各自的属性。
该测试听起来可能非常基础,但它确实测试了它需要的东西。当您以注入设置和文件系统的方式重构代码时,测试仍会告诉您文档是否设置了这些属性。
之所以称为 unit testing
,是因为您正在测试 unit
,而不是对象或方法。如果你的单位有多个类,随它去吧。不需要注入所有东西,也不需要模拟所有东西——这些东西可以简化测试,但在某些情况下最好不要模拟它们
编写测试以覆盖 100% 的代码是我们应该尝试实现的目标。但是我想出了我不知道如何测试方法(工厂方法)的情况:
public function getDocument(){
$document = new Document();
$document->settings(new Settings());
$document->filesystem(new Filesystem('e:'));
return $document;
}
该方法的目的是创建文档的快捷方式,而不是每次都写3行。
如何测试这个方法?
或者这就是为什么我们有 @codeCoverageIgnoreStart 块的情况?正是出于这个原因,PHPUnit 提供了这种注解。
编辑: 这种方法背后的主要思想是让客户的生活更轻松。仅此而已,没有配置等(但该方法将是执行此操作的好地方)。
//I don't want bother client with Settings() and Filesystem('e:')
$document = new Document(new Settings(), new Filesystem()); //NO
$document = Files.getDocument() //much easier and shorter.
//Changing API to getDocument($var, $var) make no sense, the same thing I could have normally.
$document = new Document(new Settings(),new Filesystem('e:'));
也许我应该考虑一下我是否真的应该提供那个方法,想要使用文档的用户应该知道依赖关系,它不应该被隐藏。
Inject your dependencies (Document
, Settings
, Filesystem
) via the constructor, then use test doubles视情况而定。
也重新考虑一下你的100%覆盖率政策,绝对不清楚这实际上是一件好事。
我找到了答案:此代码不可测试。
Wherever you have new XXX(...) in a method under test, you are doomed.
更多:
将依赖传递给工厂方法,在里面初始化新对象,并适当配置。在测试中,依赖项将是模拟的而不是真实的对象。
方法一
允许创建依赖项的传递工厂:
public function getDocument(SettingsFactory $sf, FilesystemFactory $ff){
$document = new Document();
$document->settings($sf->getSettings());
$document->filesystem($ff->getFilesystem());
return $document;
}
在测试中,你应该:
- 创建
Settings
实例或 mock 和一个SettingsFactory
mock 期望一次调用getSettings
并将 returnSettings
实例 - 创建
Filesystem
实例或 mock 和一个FilesytemFactory
mock 期望一次调用getFilesystem
并将 returnFilesystem
实例 - 调用
DocumentFactory
方法,传递工厂。检查Document
对象是否 returned - 检查分配给
Document
的对象是否与您为 return 配置的模拟相同
A 变体 将 getSettings
和 getFilesystem
作为 Document
工厂的方法。在那种情况下,您应该创建工厂的部分模拟,并对其设置期望。所以真正的getDocument
方法被调用了,但是当getSettings
和getFilesystem
方法被调用时,你return控制实例。
方法二
传递实际依赖项:
public function getDocument(Settings $settings, Filesystem $filesystem) {
$document = new Document();
$document->settings($settings);
$document->filesystem($filesystem);
return $document;
}
在测试中,你应该:
- 创建
Settings
实例或模拟 - 创建
Filesystem
实例或模拟 - 调用
DocumentFactory
方法,传递Settings
和Filesystem
。检查Document
对象是否 returned - 检查分配给
Document
的对象是否与您传递给工厂方法的实例相同
这个方法有什么作用? Returns 已初始化 Document
对象。因此,您只需验证返回值是一个 Document
实例,并且它已设置 Settings
和 Filesystem
对象。如果你有这些吸气剂就很容易,否则你必须访问各自的属性。
该测试听起来可能非常基础,但它确实测试了它需要的东西。当您以注入设置和文件系统的方式重构代码时,测试仍会告诉您文档是否设置了这些属性。
之所以称为 unit testing
,是因为您正在测试 unit
,而不是对象或方法。如果你的单位有多个类,随它去吧。不需要注入所有东西,也不需要模拟所有东西——这些东西可以简化测试,但在某些情况下最好不要模拟它们