10

为什么对“部分模拟”和需要它的代码有如此多的仇恨?

这是一个(理论上的)示例实现:

public ComplexResult1 operationA(Stimulus a) {
    {
        ...
        result = ...;
    }
    auditTheChange(a);
}
public ComplexResult2 operationB(Stimulus b) {
    {
        ...
        result = ...;
    }
    auditTheChange(b);
    return result;
}
void auditTheChange(Stimulus stim) {
    // do a bunch of stuff to record the change
    // and interact with another outside service
}

现在,据我了解,这是重构良好的代码。

如果我想对 operationA 和 operationB 进行 UNIT 测试,确保在每个场景中都进行审计,但不必测试审计代码的细节,我会使用部分模拟。

我没有看到/理解导致这么多项目(EasyMock、Mockito 等)推荐重构的原因是什么?

4

3 回答 3

4

如果审计确实是类的内部功能,那么代码应该作为单元测试的一部分进行测试。为什么你的班级同时处理复杂的操作和审计?审计可以转移到一个单独的班级吗?

如果是这样,将审计作为与该类的合作者引入并模拟出来。如果没有,请对其进行单元测试。

您可以使用部分模拟,但在这种情况下,我认为这表明该课程做得太多。

于 2010-09-27T22:56:24.100 回答
1

如果auditTheChange是私有函数,那么您只需不编写验证其调用的测试。您不希望您的单元测试与实现紧密耦合。

总的来说:不要对私有方法进行单元测试

于 2020-02-26T17:34:37.887 回答
0

关键点是“被测试的单元是什么”。

许多开发人员认为是 OO。在OO中,“对象”是软件的单元,因此对象也是单元测试中被测试的单元。在您的示例中, auditTheChange() 是对象中的私有方法。单元测试应该只关注公共方法的行为。模拟私有方法意味着单元测试用例处理单元的一些实现细节,这被认为是“代码气味”。

一些开发者认为软件的单位是“方法”。在您的示例中,您确实以这种方式思考。因为 operationA 和 operationB 太复杂所以你创建 auditTheChange 这样可以降低单元 operationA 和 operationB 的复杂度。如果你这样想,部分模拟不是“代码味道”,因为方法是一个被测试的单元,模拟私有方法也意味着“关注行为,而不是实现细节”。

这里没有绝对的对与错。这实际上取决于您的项目或团队的性质。我曾在一家使用 Spring 开发企业信息系统的公司工作。在 Spring 中,几乎所有的业务逻辑都是在 xxxService 对象中实现的。当方法太复杂时,我们只需将一些逻辑移至私有方法,就像您在示例中所做的那样。如果我们总是将该逻辑转移到一个新类中,将会创建大量类和对象,这将使程序难以维护。因此,我们只是将逻辑移至私有方法并使用部分模拟。实际上,很多人需要部分模拟。你可以查看这个Mockito文档。一开始,mockito 认为部分模拟是一种代码味道。经过长时间的讨论和辩论,他们现在也支持部分模拟。

于 2019-10-05T14:48:11.490 回答