“剔除”单元测试的功能

时间:2012-08-18 16:22:36

标签: c# unit-testing testing

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

即,我有一些采用以下格式的方法:

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

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

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

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

这有什么意义吗?这可能吗?

3 个答案:

答案 0 :(得分:6)

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

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

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

答案 1 :(得分:4)

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

答案 2 :(得分:3)

安德斯说的话。

一些流行的模拟框架是MoqRhinoMocks

您将更改代码,以便将记录器依赖项注入您的类:

public class ClassWithMethodsIWantToUnitTest 
{
      public ClassWithMethodsIWantToUnitTest(ILoggerService logger)
      {}

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

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

new ClassWithMethodsIWantToUnitTest(LoggerService.Instance);