【问题标题】:"Stubbing out" a function for unit tests“剔除”单元测试的功能
【发布时间】:2012-08-18 16:22:35
【问题描述】:

我想使用 Visual Studio 内置的 C# 单元测试功能对各种方法进行单元测试。事情进展得很顺利,但我遇到了一个场景,我想“存根”对特定函数调用的依赖。

也就是说,我有一些方法,格式如下:

public class ClassWithMethodsIWantToUnitTest 
{
    public void myFcn(object someArg)
    {
        ...
        LoggerService.Instance.LogMessage("myMessage");
        ...
    }
}

所以基本上,我希望我的单元测试能够简单地验证对“LogMessage”的调用是否已经发生。我不想实际检查日志文件或任何东西。我想要一种方法来查看 LoggerService 行是否已被击中并执行。

LoggerService 是一个单例,如果可能的话,我不想仅仅为了单元测试而修改代码。

根据这个问题的格式,在我看来应该可以以某种方式控制我的单元测试中的代码。换句话说,有没有办法让我制作一个伪造的 LogMessage 版本,以便我可以将它用于我的单元测试?如果可能的话,我不希望调用“真正的” LogMessage 函数。我只是想测试一下代码是否命中了调用函数的路径。

这有意义吗?这可能吗?

【问题讨论】:

    标签: c# unit-testing testing


    【解决方案1】:

    这当然是有道理的,不是一个未知的问题。

    不幸的是,您可能需要更改代码,以便它接受依赖注入。也就是说,在测试时,您应该能够注入一个特制的测试对象(模拟)而不是真实的东西。在您的情况下,这可能意味着能够将 LoggerService.Instance 设置为特殊的模拟对象。

    您需要的第二件事是您将测试的假记录器实例。您可能需要一个模拟,这是一个可以设置来检查调用者行为的特殊对象。周围有几种模拟框架,我真的建议您使用其中一种,而不是尝试使用自己的框架。

    【讨论】:

    • 我希望我能为这个问题选择两个答案。您的回答在古典意义上非常有用。接受你的回答和丹尼尔的回答是个折腾,但丹尼尔提出的“假货”概念非常有趣。
    【解决方案2】:

    Anders 的回答绝对是解决问题的“规范”方式,但在 .NET 4.5 中,还有第二种选择:

    Fakes 框架。

    它允许您向单元测试项目添加“假”程序集,该程序集接受委托来代替方法的实际实现。这是一个使用File.ReadAllText的示例

        [TestMethod]
        public void Foo()
        {
            using (ShimsContext.Create())
            {
                ShimFile.ReadAllTextString = path => "test 123";
                var reverser = new TextReverser();
                const string expected = "321 tset";
                //Act
                var actual = reverser.ReverseSomeTextFromAFile(@"C:\fakefile.txt");
                //Assert
                Assert.AreEqual(expected, actual);
            }
        }
    

    该测试方法正在做的是暂时(在 ShimsContext 的范围内)用我提供的 lambda 替换 File.ReadAllText 的实现。在这种情况下,任何时候调用ReadAllText,它都会返回字符串“test 123”。

    它比常规 DI 慢,但如果您完全依赖于单例的特定实现,那么它可能正是您所需要的。阅读更多关于它的信息here

    【讨论】:

    • +1 真的很酷,以前不知道新的假货。绝对必须阅读。
    • 这很酷,但请注意这会影响性能。在我有限的测试中,使用标准 DI 需要
    【解决方案3】:

    安德斯所说的。

    几个流行的模拟框架是MoqRhinoMocks

    你会改变你的代码,以便将记录器依赖注入你的类:

    public class ClassWithMethodsIWantToUnitTest 
    {
          public ClassWithMethodsIWantToUnitTest(ILoggerService logger)
          {}
    
          public void myFcn(object someArg)
          {
             ...
              logger.LogMessage("myMessage");
             ...
          }
    }
    

    或类似的东西。 DI 框架可以在需要时将记录器自动注入到类中。本质上是一个 DI 框架自动调用

    new ClassWithMethodsIWantToUnitTest(LoggerService.Instance);
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-09-07
      • 1970-01-01
      • 2021-08-18
      • 2017-07-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多