何时使用存根/模拟以及何时在单元测试中使用真实对象?

时间:2018-01-09 20:42:12

标签: php unit-testing mocking phpunit

我最近试图提高我的单元测试技能,并阅读了一些关于单元测试的文献,我也在努力实现我在php项目中学到的东西,我目前用phpunit开发。但我仍然有一个非常基本的问题,即如何将与其他类的对象进行交互的单元测试方法,甚至与同一类的其他方法进行交互。

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

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私有方法中可能出现的所有可能影响的每个公共方法吗?或者我应该只在一个使用它的公共方法中测试所有可能的效果,并在测试中为所有其他公共方法存根私有方法?
  4. 我不确定何时使用存根/模拟,何时不使用。

2 个答案:

答案 0 :(得分:2)

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

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

答案 1 :(得分:0)

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

从目标中可以清楚地看出 - 您是单元测试逻辑本身而不是数据库服务数据获取,因此您将模拟数据库服务并假设数据库服务提供正确的数据&你只需要专注于测试计算数据的逻辑。您将对数据库提取服务进行单独的测试,并且隔离属性就是全部。

单词 unit 在这个意义上非常重要,因为您的这些测试应该只关注当前的逻辑,只能限制您的范围和范围。不要把其他一切都弄乱。

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

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

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