如何使用 rhino mock 测试 class 的私有方法是否被调用?

How can I test if a private method of a class is called or not with rhino mock?

我对 C# 和 rhino mocks 还很陌生。我搜索并找到了与我的问题相似的主题,但找不到合适的解决方案。

我想了解在我的单元测试中是否调用了私有方法。我正在使用 rhino mock,阅读了很多关于它的文件,其中一些只是说将方法的访问说明符从 private 更改为 public,但我无法更改源代码。我尝试将 link 源文件添加到我的测试项目,但它没有改变。

  public void calculateItems()
    {
        var result = new Result(fileName, ip, localPath, remotePath);

        calculateItems(result, nameOfString);
    }

    private void calculateItems(Result result, string nameOfString )

正如您从上面的代码中看到的,我有两个名称完全相同的方法,calculateItems,但是 public 一个没有参数,一个 private 有两个参数。我试图了解当我在单元测试中调用 public 时,是否调用了私有方法?

    private CalculateClass sut;
    private Result result; 

    [SetUp]
    public void Setup()
    {
      result = MockRepository.GenerateStub<Result>();
      sut = new CalculateClass();
    }

    [TearDown]
    public void TearDown()
    {

    }

    [Test]
    public void test()
    {     
        sut.Stub(stub => stub.calculateItems(Arg<Result>.Is.Anything, Arg<string>.Is.Anything));

        sut.calculateItems();

        sut.AssertWasCalled(stub => stub.calculateItems(Arg<Result>.Is.Anything, Arg<string>.Is.Anything));
    }

在我的单元测试中,我遇到了这样一个错误 "No overload method for calculateItems take two arguments"。有没有办法在不更改源代码的情况下对其进行测试?

你测试错了。私有方法是私有的。它们与使用代码无关,并且单元测试像其他任何东西一样使用代码。

在您的测试中,您测试并验证组件的外向功能。它的内部实现细节与测试无关。所有测试关心的是调用的操作是否产生预期的结果。

所以你必须问自己的问题是...调用此操作时的预期结果是什么?:

calculateItems()

它return什么都没有,那么它有什么作用呢?它以某种方式修改什么状态? 是你的测试需要观察的,不是实现细节而是可观察的结果。 (如果操作 没有 可观察到的结果,那么 "passed" 或 "failed" 之间没有区别,所以没有什么可测试的。)

我们看不到您的代码的详细信息,但观察结果可能完全耦合到另一个组件。如果是这种情况,那么其他组件就是此操作的依赖项,单元测试的目标是模拟该依赖项,以便可以独立于依赖项测试操作。然后可能需要修改组件,以便提供依赖性而不是内部控制。 (这被称为 Dependency Inversion Principle。)


也值得注意...

but I can not change the source code

这完全是一个单独的问题。如果您真的不能 更改源代码,那么这些测试的价值将大大降低,甚至可能完全消除。如果测试失败,你能做些什么?没有什么。因为你不能更改代码。那么你在测试什么?

请记住,程序员编写 不能 有意义的单元测试的代码不仅可能而且很不幸 非常普遍 .如果此代码是由其他人提供给您的,并且由于某些非技术原因禁止您更改它,那么其他人将负责更正代码。 "Correcting" 可能包括 "making it possible to meaningfully unit test"。 (或者,老实说,他们 应该对其进行单元测试。不是你。)

如果您的 public 方法调用您的私有方法,那么在您的测试中也会发生同样的事情。测试只不过是可以 运行 和调试的代码,您可以尝试一下,看看会发生什么。

私有方法无法直接测试,但可以通过它们的 public 调用者进行测试,这就是您正在做的,所以一切都很好。拥有这样的设置是否是个好主意,那完全是另一回事,但我现在不打算讨论它。

现在,让我们讨论一下您实际测试的是什么。

单元测试不应该对他们测试的代码有深入的了解。原因是你应该有输入和输出,你不应该关心中间发生了什么。

如果您重构代码并消除私有方法,那么即使您对 public 方法的输入和输出保持不变,您的测试也会中断。这不是一个好的位置,这就是我们所说的脆弱测试。

所以围绕 public 方法添加你的功能测试,验证你得到了你期望的帽子,不要担心它是否调用你的私有方法。

当你说你需要知道你的私有方法是否被调用时,这可以有两种不同的解释:

  1. 您想确保在一个特定测试中调用私有方法,使其成为该测试的成功标准。

  2. 您想知道您的任何测试用例是否调用了私有方法。您可能对此感兴趣,因为您想确定私有方法是否包含在您的测试套件中,或者如您所说,只是为了形成对代码中实际发生的事情的理解。

关于第二种解释:如果你想了解代码中发生了什么,一个好的方法是使用调试器并单步调试代码以查看什么函数被调用。由于我不是这里的 C# 专家,我不能推荐任何特定的调试工具,但在网上找到一些关于此的推荐应该不难。这种方法将满足您的要求,不需要更改源代码

另一种可能性,特别是如果您对测试是否覆盖您的私有函数感兴趣,是使用 C# 的测试覆盖工具。覆盖工具会告诉你私有方法是否被调用。同样,这不需要对源代码进行任何更改。

关于您问题的第一个解释:如果您想测试是否调用了某个私有函数作为测试成功标准的一部分,您最好使用使用public API。那么,在这些测试中,你应该可以判断是否调用了私有函数,因为私有函数对测试结果的影响。

而且,与其他意见相反,您应该测试实施。单元测试的主要目标是找到代码中的错误。不同的实现有不同的错误。这就是为什么人们也使用覆盖工具,看看他们是否已经覆盖了他们实现的代码。而且,覆盖率还不够,您还需要检查表达式的边界情况等。当然,进行可维护的测试和在重构的情况下不会不必要地中断的测试是很好的目标(为什么要通过 public API 通常是一个好方法 - 但并非总是如此),但与找到所有错误的目标相比,它们是次要目标。