测试类具有高内聚性但逻辑相当复杂

时间:2017-10-13 09:57:51

标签: php unit-testing testing

例如,我有一个类,它接收原始数据,分析它并返回报告的数字。我们称之为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
}

因此,该课程实际上只做了一件事,一步一步地处理某些报告的原始数据。它不大,最有可能4-6种方法,5-10行。它的所有方法都是紧密相关的,并且有一个目的,所以我会说它具有很高的凝聚力。但是测试课程的最佳方法是什么?

如果我尝试用心态“测试行为,而不是实现”来接近它,我应该只测试它的单一公共方法。这种方法的优点是,以后很容易重构该类,甚至没有触及测试,我相信它仍然会表现出完全相同的行为。但是通过单一方法,太多可能的代码路径来覆盖所有情况也很难。

我可以将大多数(可能是全部)方法公开并单独测试它们,但随后我打破了类和算法的封装并测试了它的实现细节。任何重构都会更加困难,并且更有可能发生不必要的行为变化。

作为最后一个选项,我可以把它分成小类,最有可能有1或2种方法。但是,将高度紧密的类分解为更小,紧密耦合的类,这是非常好的主意,它们做了非常具体的事情并且不会在其他地方重复使用吗?重构它仍然会更加困难。并且其他开发人员可能更难以快速全面了解其工作原理。

1 个答案:

答案 0 :(得分:1)

我总是试着尽可能多地拆分课程,就像你在上一个选项中所说的那样,因为根据我的经验,这是简化测试的最佳方法,这本身就是一个非常正确的理由... <登记/> 另外,我不同意你的意见 - 我认为这种方法可以让你在将来更容易重构,更容易理解,而不是一个包含所有内容的大班......

查看您的代码,我可以看到一堆独立的“角色”:ReporterInterfaceReporterReportDataPreparatorThisDoerThatDoer,{ {1}}(显然你可以找到更好的名字:)。

您可能希望将来重用其中一些,但即使不是这种情况,并且所有这些都非常特定于报告,您只需将它们放在单独的命名空间和文件夹中(如“报告模块“)。
此报告模块有一个唯一的API,即ReportPostProcessor,系统的其余部分只需要关心该接口,无论该报告者是否使用私有方法,其他类或整个系统。背景 - 他们只需要致电ReporterInterface ...

因此,从系统其他部分的角度来看,没有任何变化,您的报告服务仍然在一起,您的单元测试很多更容易编写和维护......