【问题标题】:How should I test a public method call to another protected method?我应该如何测试对另一个受保护方法的公共方法调用?
【发布时间】:2021-10-23 11:36:10
【问题描述】:

我有一堂课:

class a
{
    public function b()
    {
        $arg1 = $this->service1->getArg1();
        $arg2 = $this->service2->getArg2();
        $result = $this->d($arg1, $arg2);
        if ($result) {
            return true;
        }
        return false;
    }

    public function c()
    {
        $arg1 = $this->service3->getArg1();
        $arg2 = $this->service4->getArg2();
        $result = $this->d($arg1, $arg2);
        if ($result) {
            return true;
        }
        return false;
    }
    
    protected function d($arg1, $arg2)
    {
        if ($arg1 == $arg2) {
            // do something
            // call service 5, service 6
            return $result;
        }
        // do something
        // call service 5, service 6
        return null;
    }
}

公共函数 bc 都调用私有函数 d。如果我为函数 bc 编写测试,我将至少有 2 种情况:每个函数测试的“arg1 等于 arg2”和“arg1 不等于 arg2”。以下哪个解决方案是正确的? :

  • 解决方案 1:函数testb 有 2 个案例,我必须为服务 5,6 创建模拟。然后函数testc我有2个案例,也为服务5,6创建模拟。我发现这是非常重复的工作量,因为我必须“通过”函数 d 两次。
  • 解决方案 2:我不在乎 $arg1$arg2 的值。我模拟函数 d,期望它返回我想要的任何值。然后我最后我会写一个函数testd。但我没有这个解决方案的技术。
  • 解决方案 3:将函数 d 分离到一个新类 serviceD 并公开它。然后将serviceD 注入类a。现在功能 bc 喜欢:
$result = $this->serviceD->d($arg1, $arg2);

我很容易模拟 serviceD。但是,如果我不想创建新课程?我仍然想使用解决方案 2。

【问题讨论】:

    标签: php unit-testing phpunit


    【解决方案1】:

    我想指出一件事。由于您似乎已经拥有 SUT(被测系统)代码,因此您知道代码的外观,因此您尝试基于此做出决定。

    这在技术上是错误的,从我的角度来看是一个不行。您不应该根据代码“形状”和实现来编写测试。您应该考虑一个函数(类客户端的公共 API)在输入和输出方面做了什么;实现总是一个细节,你不应该依赖它,因为它可以改变,如果测试是这样编写的,事情很快就会变得很麻烦。想象一下,当您的代码的行为没有改变,但实现发生了变化:每次 SUT 代码实现改变时,您最终都会(几乎)改变测试代码。 p>

    话虽如此,唯一“正确”的答案是我们没有所有细节来提供一个好的答案。也许 SUT 类做了“太多”,违反了 SRP,所以解决方案是拆分代码。也许,如果您考虑的是行为而不是实现,那么答案已经存在。以此类推。

    但是,如果您想对所提出的解决方案提出个人意见,请记住在这种情况下的高级抽象,我会说:

    解决方案 1:如果 SUT 没有理由被拆分,您应该不关心 function d 在其实现方面,而是在它的作用方面。在那之后,你应该“不要看”代码,如果function bfunction c 的行为是相同的(除了service3service4),你应该“接受”一种重复作为测试工作的“设置”。

    解决方案 2:仅当您为在 function d 中具有许多不同执行路径的遗留代码编写测试时,我才会这样做,并且您知道您将对其进行重构。在这种情况下,您希望在最低程度上减少所有可能的执行路径,因为为您提供价值的是 function bfunction c 行为,并且您知道该类将被重构。

    解决方案 3:如前所述,这取决于上下文。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-05-22
      • 1970-01-01
      • 2015-02-26
      • 2020-08-02
      • 2017-01-20
      • 2012-01-26
      • 2019-02-13
      相关资源
      最近更新 更多