例如,我有一个采用原始数据,分析数据并返回报告数字的类。让我们把它SomeReportDataProvider具有高凝聚力但相当复杂的逻辑的测试课程
class SomeReportDataProvider
{
public function report(array $data)
{
$data = $this->prepare($data);
$report = [];
foreach ($data as $item) {
if ($row->somefield == 'somevalue') {
$report = $this->doThis($report, $row);
} else {
$report = $this->doThat($report, $row);
}
}
// ... do something else
$report = $this->postProcess($report);
return $report;
}
protected function doThis($item)
{
// ... do some math, probably call some other methods
}
protected function doThat($item)
{
// ... do other math
}
// ... and so on
}
所以该类确实只有一件事,一步一步的一些报告的原始数据的等待处理。这不是很大,最有可能的方法有5-10行。它的所有方法都是紧密相关的,并且有一个目的,所以我认为它具有很高的凝聚力。但是什么是测试班级的最佳方式?
如果我尝试的心态“测试行为,不执行”办法吧,我应该测试仅是单一的公共方法。这种方法的优点在于,即使未触及测试,也可以很容易地重构类,并且我确信它仍然具有完全相同的行为。但是通过单一方法覆盖所有情况也可能非常困难,可能的代码路径太多。
我可以让大部分(可能全部)的方法public和隔离测试他们,但后来我打破阶级和算法的封装和测试它的实现细节。任何重构都将变得更加困难和不必要的行为变化。
作为最后一种选择,我可以将它分成小类,最有可能有1或2种方法。但是将高度凝聚力的课堂分成更小,紧密耦合的课程,是否真的是一个好主意,这些课程做的非常具体,并且不会在其他地方重复使用?而且重构仍然会更困难。对于其他开发人员来说,可能很难快速了解它的工作原理。
那我们不会得到一个有太多依赖的类吗?另外,我们是否应该模拟/存留所有测试?如果我们这样做,我们会得到很多嘲笑,安排它们需要很多时间,而且很多人都不是最好的测试方式。如果我们不嘲笑,那么仍然存在许多代码路径的问题... – Dmitri
我想这是一个味道问题,很大程度上取决于情况 - 个人而言,我更喜欢太多的代码而不是太多的功能一个班级(无论如何,你可以用一种并不是所有的记录都进入记者班但是在其他记录里)的方式来组织结构。至于模拟,我的方法通常是在测试中嘲笑_everything_,但正在测试的类......这是唯一的正确_unit_测试事情的方法......如果你想测试整个“模块”的真实行为,一起工作,您可以添加单独的集成测试,但对于单元集成测试,这是我至少做的 – MikO