如何使用Rhino Mock测试是否调用了类的私有方法?

时间:2019-03-01 13:47:24

标签: c# unit-testing private-class

我在C#和犀牛模拟游戏方面还是很新的。我搜索并找到与我的问题类似的主题,但找不到合适的解决方案。

我试图了解在单元测试中是否调用了私有方法。我正在使用Rhino模拟,阅读了许多有关它的文件,其中一些只是说将方法的访问说明从私有更改为公共,但是我无法更改源代码。我试图将源文件链接到我的测试项目,但它没有改变。

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

        calculateItems(result, nameOfString);
    }

    private void calculateItems(Result result, string nameOfString )

从上面的代码中可以看到,我有两种方法具有完全相同的名称,calculateItems,但公共方法没有参数,私有方法有两个参数。我想了解我在单元测试中何时调用public时,是否调用了private方法?

    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));
    }

在我的单元测试中,我遇到这样的错误,该错误为“ computeItems的重载方法不带有两个参数”。有没有一种方法可以对其进行测试而无需更改源代码?

3 个答案:

答案 0 :(得分:4)

您正在测试错误的内容。私有方法是私有的。它们与使用代码无关,单元测试也与其他代码一样使用代码。

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

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

calculateItems()

它不返回任何内容,那么会做什么呢?它以某种方式修改到什么状态? 是您的测试需要观察的内容,而不是实现细节,而是可观察的结果。 (而且,如果操作没有可观察到的结果,则“通过”或“失败”之间没有区别,因此没有要测试的东西。)

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


还要注意...

  

但我无法更改源代码

那完全是一个单独的问题。如果您确实不能更改源代码,那么这些测试的价值将大大降低,甚至有可能被完全消除。如果测试失败,您该怎么办?没有。因为您无法更改代码。那您要测试什么?

请记住,程序员编写不能进行有意义的单元测试的代码不仅可能而且很不幸非常普遍。如果此代码是由其他人提供给您的,并且由于某些非技术原因而被禁止更改,那么其他人有责任更正该代码。 “更正”可以包括“使有意义地进行单元测试成为可能”。 (或者说实话,他们应该对其进行单元测试。不是您。)

答案 1 :(得分:0)

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

不能直接测试私有方法,但是可以通过公共调用程序来测试它们,这就是您正在做的事情,所以这一切都很好。拥有这样一个设置是否是一个好主意,完全是另外一个故事,但是我现在不打算讨论。

现在,让我们讨论一下您实际测试的内容。

单元测试不应对他们测试的代码有深入的了解。原因是您应该具有输入和输出,并且您不必理会这之间的情况。

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

因此,请在公共方法周围添加功能测试,确认您已获得期望,不要担心它是否会调用您的私有方法。

答案 2 :(得分:0)

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

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

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

关于第二种解释:如果您想了解代码中正在发生的事情,一个好的方法是使用调试器,然后单步执行代码以查看调用了什么函数。因为我不是C#专家,所以我不推荐任何特定的调试工具,但是在Web上找到关于此的一些建议应该并不困难。这种方法可以满足您的要求,而无需更改源代码

另一种可能性,特别是如果您对您的私有功能是否受测试感兴趣,尤其是对C#使用测试覆盖率工具。覆盖率工具将显示是否调用了私有方法。同样,这不需要对源代码进行任何更改。

关于您的问题的第一种解释:如果您要测试是否将某些privat函数作为测试成功标准的一部分,则最好使用使用公共API的测试来进行此操作。然后,在这些测试中,由于私有函数对测试结果的影响,您应该能够判断是否调用了私有函数。

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