你是否应该用单独的单元测试来覆盖私有方法

Should you cover private methods with a separate unit test

我习惯了单元测试中的一种方法,即用单独的单元测试来覆盖每个方法。 通过将私有方法标记为内部虚拟 (C#) 并为它们创建单独的单元测试来包括私有方法。最近我在我的公司找不到支持这种方法的人——首选方法是为 public 方法编写单个单元测试,即使它包含对另一个私有方法的调用,在在这种情况下,这些私有方法中的所有依赖项也必须被模拟。我将举一个简单的例子:

public async Task DoSomeWorkAsync()
{
    await repo.SaveStateAsync();
    await DoSomeAdditionalWorkAsync();
    Console.WriteLine("Done");
}

private async Task DoSomeAdditionalWorkAsync()
{
    await repo.GetDataAsync();
    await repo.GetAnotherDataSetAsync();
}

在上面的示例中,我的同事说正确的做法是为所有 3 个被调用的方法(SaveState、GetData、GetAnotherData)设置存根,这样单个单元测试将同时执行 public 和私有方法并检查两者。 我习惯这样做的方式是将方法声明为内部虚拟,因此方法将对带有测试的程序集可见,并首先为 DoSomeAdditionalWorkAsync 方法编写单独的测试,然后在编写另一个单元时使用部分模拟来模拟 DoSomeAdditionalWorkAsync 方法测试 DoSomeWorkAsync 方法。

我听说如果私有方法只在一个地方使用并且是出于代码可读性的原因而创建的,那么进行单个单元测试就可以了。但在那种情况下,下一位开发人员可能会重新使用私有方法并断言它已经包含在单独的测试中。 答案是否取决于私有方法本身的复杂性? (私有方法越大,添加单元测试的理由就越多)

我错了吗?我应该坚持同事的建议吗?我试图搜索此主题,但不确定要在此处查找哪些关键字。对于这样一个简单的场景,什么是最好的 practices/recommendations?

提前感谢您提出任何意见或建议。

简短的回答是你不应该直接测试私有方法,而应该只测试它们对调用它们的 public 方法的影响。单元测试是被测对象的客户端,就像代码中依赖于该对象的其他 classes 一样。事实上,如果你正在练习 test-driven 开发(TDD),单元测试就是你对象的第一个客户。测试应该只访问 class' public 接口。如果一个对象很难通过其 public 接口进行测试,那么它就很难在生产代码中使用。这是单元测试暴露潜在设计问题的众多示例之一。

想法是您的 class 公开了一个接口,您的测试从该接口验证 input/output 是正确的。每个 class/method 都应该有一个单一的责任。

您描述的将方法标记为受保护虚拟的技术是 I have used myself on occasion,但这是在大型遗留代码应用程序的上下文中,在该应用程序中立即将所有内容重构为可测试状态是不切实际的.应谨慎使用此技术。

一般来说,如果您觉得私有方法足够复杂以至于需要自己的测试,您应该将其提取到自己的 class。如果私有方法没有太多复杂性,那么您将不必要地断言实现细节并使您的代码将来难以重构。

如果您需要设置许多模拟对象来测试单个 class 那么这可能违反了 SRP。编写测试的一大副作用是它会突出显示任何设计不当的 classes.

强烈推荐这本书给你:https://www.artofunittesting.com/