【问题标题】:When to use stubs/mocks and when to use real objects in unit testing?何时使用存根/模拟,何时在单元测试中使用真实对象?
【发布时间】:2018-06-18 23:17:14
【问题描述】:

我最近试图提高我的单元测试技能并阅读了很多关于单元测试的文献,我还试图了解我在我目前正在使用 phpunit 开发的一个 php 项目中学到了什么。但是在我看来,我仍然有一个非常基本的问题,如何对与其他类的对象甚至与同一类的其他方法交互的方法进行单元测试。

是否有一些经验法则或一些帮助我可以决定我应该存根/模拟哪些依赖项以及哪些依赖项我应该简单地使用普通对象?为了澄清我的问题,这里有一个示例代码,具有不同的场景:

interface DependencyInterface {
    public method dependentMethod() { ... }
}
class Dependency implements DependencyInterface {...}

class ClassUnderTest {
    private $dependency
    public __construct(DependencyInterface $dependency) {
        $this->dependency = dependency;
    }
    public function methodUnderTest() {
        ...
        $result1 = $this->dependency->dependentMethod();
        ...
        $result2 = $this->otherMethod();
        ...
        $result3 = $this->usedInMultiplePublicMethods();
    }

    public function otherMethod() {...}
    private function usedInMultiplePublicMethods() {...}
}

所以我现在的问题是针对测试方法 methodUnderTest 的功能的单元测试,我应该:

  1. 存根接口 DependencyInterface 并将其注入构造函数,还是我应该简单地使用实现 Dependency 的实例?
  2. 部分存根 ClassUnderTest 类本身,以便为 otherMethod 提供固定结果,因为这个可能非常复杂的方法已经拥有自己的完整单元测试?
  3. 我决定不对私有方法进行单元测试,因为它们不是类接口的一部分(我知道这是一个有争议的话题,不在我的问题范围内)。我现在是否必须为每个使用私有方法 usedInMultiplePublicMethods 的公共方法涵盖私有方法中可能发生的所有可能影响?还是应该只在使用它的一种公共方法中测试所有可能的影响,并在所有其他公共方法的测试中存根私有方法?

我不太确定何时使用存根/模拟,何时不使用。

【问题讨论】:

    标签: php unit-testing mocking phpunit


    【解决方案1】:

    模拟的原因是能够编写单元测试,这意味着测试是:快速、隔离、可重复、自我验证和彻底及时(F.I.R.S.T

    为了能够单独测试单元/模块,您可能需要模拟/存根任何外部模块(数据库访问、api 调用、日志记录系统...)。

    【讨论】:

    • 好的,我明白了,这意味着如果我在其他类中没有任何外部影响的“正常”逻辑,我不应该存根它们来测试我的方法并简单地使用普通类?但例如,如果我有一个接口(如我的示例 DependencyInterface),其中没有一个是通用的实现,我是否只选择其中一个用于我的测试用例?
    • 您正在测试的意图是否包括此依赖项中的实现细节?在这里,我们可能处于一个更主观的领域,但一个好的单元测试是 UNIT(这是最小的有意义的代码)。你测试多少个类/方法并不重要,只要它是小的、有意义的和可预测的......我会说如果你有一个接口并且你可以测试你的意图我会模拟接口......它减少了不确定性。
    【解决方案2】:

    对于您的第 1 点和第 2 点,rad 的回答指出了要牢记的主要基本原则,例如如果您要测试使用数据库服务获取数据然后对获取的数据进行计算的逻辑,您会模拟该数据库服务还是使用真正的数据库服务?

    从客观上很清楚 - 您是对逻辑本身的单元测试,而不是数据库服务数据获取,因此您将模拟数据库服务并假设数据库服务正在提供正确的数据,您只需专注于测试计算数据的逻辑。您将对数据库获取服务进行单独的测试,并且隔离属性就是全部。

    unit 这个词在这个意义上很重要,你的这些测试应该非常关注当前的逻辑,只有通过限制你的范围并且不要把其他所有东西都混入其中。

    这个答案主要是针对您的第 3 点。不明确测试私有方法是可以的,但是如果您按照单元测试的基本目的进行 - 您不会太担心某些东西是私有的还是公共的。在某个地方,单元测试也是为了开发人员的自我满足,如果单元测试也是为私有方法编写的,它只会让你的代码更加健壮。

    仅仅因为访问级别是私有的,并不会改变它是一个需要测试的逻辑的基本概念。明智的代码覆盖率,您可能对一种公共方法没问题,但我认为您应该将来自不同公共方法的调用视为不同的。

    永远不要忘记单元测试的基本目的——你试图在你的逻辑中发现错误,试图覆盖所有边界情况并试图让你的代码更健壮。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-03-29
      • 2010-11-28
      相关资源
      最近更新 更多