【问题标题】:How to write unit test for private method in c# using moq framework?如何使用 moq 框架在 c# 中为私有方法编写单元测试?
【发布时间】:2014-10-03 23:14:20
【问题描述】:

我想使用 moq 框架为 C# 中的私有方法编写单元测试,我在 StackOverFlow 和 Google 中进行了搜索,但找不到预期的结果。如果可以,请帮助我。

【问题讨论】:

    标签: c# unit-testing moq


    【解决方案1】:

    你不能,至少起订量不能。

    但更重要的是,您不应该。 首先,你不测试方法,你测试行为。其次,为了测试行为,您可以练习一个类型的 public API 并验证该练习的结果。

    私有方法是实现细节。您不想验证 如何 完成事情,您想验证事情 所做 是否完成。

    【讨论】:

    • 实现单一职责原则并将有问题的私有方法重构到自己的类中不是更合适吗?然后你测试每个类的公共行为。
    • 在处理新领域时的好建议。棕地开发没有那么奢侈。你如何测试一个有 3000 行代码、30 个具体依赖项(包括静态和无 DI)的类?我需要一个比这更灵活的工具。 Microsoft Fakes 似乎拥有大部分(如果不是全部)此功能。但它仅在 Visual Studio 2017 Enterprise 中可用。我需要一个免费工具。
    • 我在围栏的另一边。私有函数应作为单元测试进行正确性测试。一个好处是错误将您指向实际的错误代码,而不是使用您知道多长时间的私有函数的某个块。我只是希望更多的图书馆支持这一点。
    • 但是私有方法涉及一些应该被模拟的过程并不少见,例如网络操作。这就是模拟应该发挥作用并使开发人员受益的部分。
    • @dcastro 那么你建议如何修复涉及私有方法内的网络操作的设计?
    【解决方案2】:

    在你项目的 AssemblyInfo.cs 中添加

    [assembly: InternalsVisibleTo("Namespace.OfYourUnitTest.Project")]
    

    然后你将方法设为内部而不是私有。

    它的好处是避免公开。

    然而,正如 dcastro 所指出的,有些人强烈反对这种测试方式。

    【讨论】:

      【解决方案3】:

      也许您不应该(请参阅其他答案了解原因),但您可以使用Microsoft's Visual Studio Test Tools 执行此操作。下面给出一个简化的例子。

      给定您要测试的以下类:

      public class ClassToTest
      {
          private int Duplicate(int n)
          {
              return n*2;
          }
      }
      

      您可以使用以下代码来测试私有Duplicate方法:

      using Microsoft.VisualStudio.TestTools.UnitTesting;
      
      // ...
      
      [TestMethod]
      public void MyTestMethod()
      {
          // Arrange
          var testClass = new ClassToTest();
          var privateObject = new PrivateObject(testClass);
      
          // Act
          var output = (int) privateObject.Invoke("Duplicate", 21);
      
          // Assert
          Assert.AreEqual(42, output);
      }
      

      【讨论】:

        【解决方案4】:

        简单地说,你不知道。私有方法对其他类不可见。

        有很多方法可以解决这个问题:

        • 将私有视为您正在测试的方法的一部分,在他们的单元测试中涵盖它。将公共方法视为黑匣子并测试它们的操作。
        • 使其受到保护并从您正在测试的类继承您的测试类(或使用部分相同的想法)
        • 将其公开(如果您正在对接口进行编码,则实际上不会将其公开给您的消费者)

        对于公共方法(选项三),可以部分模拟可以替换方法的类。在起订量中,您可以这样做:

        var moq = new Mock<MyClass>();
        moq.CallBase = true;
        moq.Setup(x => x.MyPublicMethodToOverride()).Returns(true);
        

        还有更多详情here

        【讨论】:

          【解决方案5】:

          Moq 支持模拟 protected 方法。 将方法更改为protected,而不是private,将允许您模拟它们的实现。

          以下来自Moq Quickstart Documentationdeep link):

          为受保护成员设置期望(您无法获得 IntelliSense 对于这些,因此您可以使用成员名称作为字符串访问它们)。 假设以下具有受保护函数的类应该是 嘲讽:

          public class CommandBase {
              protected virtual int Execute();            // (1) 
              protected virtual bool Execute(string arg); // (2)
          }
          // at the top of the test fixture
          using Moq.Protected;
          
          // In the test, mocking the `int Execute()` method (1) 
          var mock = new Mock<CommandBase>();
          mock.Protected()
               .Setup<int>("Execute")
               .Returns(5);
          
          // If you need argument matching, you MUST use ItExpr rather than It
          // planning on improving this for vNext (see below for an alternative in Moq 4.8)
          // Mocking the `bool Execute(string arg)` method (2)
          mock.Protected()
              .Setup<bool>("Execute",
                  ItExpr.IsAny<string>())
              .Returns(true);
          

          Moq 4.8 及更高版本允许您通过 完全不相关的类型,具有相同的成员,因此提供 IntelliSense 工作所需的类型信息。捡起 从上面的项目符号点的例子,你也可以使用这个接口 设置受保护的泛型方法和具有 by-ref 的方法 参数:

          // Completely unrelated Interface (CommandBase is not derived from it) only created for the test.
          // It contains a method with an identical method signature to the protected method in the actual class which should be mocked
          interface CommandBaseProtectedMembers
          {
              bool Execute(string arg);
          }
          
          mock.Protected().As<CommandBaseProtectedMembers>()
              .Setup(m => m.Execute(It.IsAny<string>()))  // will set up CommandBase.Execute
              .Returns(true);
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2016-06-29
            • 2013-05-25
            • 2017-02-03
            • 1970-01-01
            • 2018-03-29
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多