Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/php/270.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Php 具有高内聚性但逻辑相当复杂的测试类_Php_Unit Testing_Testing - Fatal编程技术网

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

Php 具有高内聚性但逻辑相当复杂的测试类,php,unit-testing,testing,Php,Unit Testing,Testing,例如,我有一个类,它获取原始数据,对其进行分析,并为报表返回数字。让我们称之为某个ReportDataProvider class SomeReportDataProvider { public function report(array $data) { $data = $this->prepare($data); $report = []; foreach ($data as $item) { i

例如,我有一个类,它获取原始数据,对其进行分析,并为报表返回数字。让我们称之为某个ReportDataProvider

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个方法。但是,将高内聚性类划分为更小的、紧密耦合的类,这些类做非常具体的事情,并且不在其他任何地方重用,这真的是一个好主意吗?而且,重构仍然会更加困难。对于其他开发人员来说,很难快速全面了解它的工作原理。

我总是尝试尽可能多地拆分类,就像您在上一个选项中所说的那样,因为根据我的经验,这是简化测试的最佳方法,这本身就是一个非常有效的理由……
另外,我不同意你的观点——我认为这种方法使将来的重构更容易,也更容易理解,而不是一个包含所有东西的大型类

查看您的代码,我可以看到一组单独的“角色”:
ReporterInterface
Reporter
ReportDataPreparator
ThisDoer
ThatDoer
ReportPostProcessor
(显然,您可以找到更好的名称:)

您可能希望在将来重用其中一些,但即使不是这样,而且所有这些都非常特定于报表,您也可以将它们放在单独的命名空间和文件夹中(如“报表模块”)。
此报告模块有一个独特的API,即您的
ReporterInterface
,系统的所有其余部分只需关心该接口,而不管该报告程序是在后台使用私有方法、其他类还是整个系统-他们只需调用
$reporter->report($data)


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

那么,我们会得到一个依赖性太多的类吗?此外,我们是否应该在测试中模拟/存根所有这些文件?如果我们这样做,我们最终会得到很多模拟,安排它们需要很多时间,而根据很多人的说法,这并不是最好的测试方式。如果我们不模仿,那么仍然存在许多代码路径的问题……我想这是一个品味的问题,很大程度上取决于情况——就我个人而言,我更喜欢太多的DEP,而不是单个类中太多的功能(无论如何,你可以用一种不是所有的DEP都进入reporter类的方式来构建东西,而是在其他DEP中)。至于模拟,我的方法通常是模拟测试中的所有内容,但被测试的类。。。这是正确进行单元测试的唯一方法。。。如果您想测试整个“模块”协同工作的真实行为,您可以添加单独的集成测试,但对于单元测试,至少我是这么做的