【问题标题】:What is the name of the pattern?图案的名称是什么?
【发布时间】:2010-12-08 09:22:22
【问题描述】:

我经常设计系统,我的系统中有一个类,它有一堆受保护的方法。受保护的方法是因为它们应该可以被专业化访问,因此,我认为受保护的方法是我的通用类和任何专业类之间的契约。

因此,我想测试这些方法的行为是否正确,因此我的单元测试将需要访问受保护的方法,这在 c# 中是不可能的,除非使用像反射这样的脏黑客。

我通常选择的解决方案是在我的单元测试中创建一个专门的类,将这些受保护的方法公开。因为我总是将单元测试保存在单独的程序集中,所以系统代码本身无法访问这些类,并且在我的系统代码中,受保护的函数仍然受到保护。

// In the system code assembly
namespace MySystem {
    public class SomeClass {
        protected void DoSomething() {
            ...
        }
    }
}

// In the unit test assembly
namespace MySystem.Unittests {
    public class SomeClass2 : SomeClass {
        public new void DoSomething() {
            base.DoSomething();
        }
    }
}

我的单元测试现在能够通过实例化专用类来测试父类上受保护成员的行为。

我还进一步使用它来模拟对象并模拟对受保护的虚拟函数的函数调用,但该代码有点复杂,所以我将把它删掉。

我发现完成这项任务的这种方式非常简单,以至于我不可能是唯一这样做的人,所以我认为必须有一个通用的命名约定?

【问题讨论】:

  • 我只使用接口并在测试时不理会私有方法。如果您遵循 SRP,它们不应该是太多。
  • 我会将 SomeClass2 重命名为 TestableSomeClass。这阐明了原因和内容。 (而且,是的,我们在这里经常使用这种模式......)
  • @Arjan - 我的问题之一是弄清楚如何调用 SomClass2 ;)

标签: c# unit-testing tdd naming-conventions design-patterns


【解决方案1】:

不知道你在做什么的模式名称,但是 .net 对此有一个解决方案...使方法受内部保护,然后将以下内容添加到项目的 assemblyInfo 文件中

[assembly: InternalsVisibleToAttribute("[FQN of TestAssembly]")]

这将使项目中的内部方法对属性中指定的测试程序集可见

【讨论】:

    【解决方案2】:

    我不确定我是否明白这一点,一般来说,在测试您的受保护方法时,我是从 TDD 的角度说的。通常,您的测试是针对类的公共方法编写的。当您重构代码以提取内部使用的常用方法时,就会出现受保护的方法。由于这些来自重构,因此您现有的测试已经涵盖了正在重构的代码。稍后通过专业化的公共方法对使用重构方法的代码进行测试也会测试代码——在这种情况下,专业化的公共方法在使用通用的受保护方法时可以正常工作。

    在少数情况下,对受保护方法的显式测试是有意义的,我认为使用反射进行测试没有什么害处。毕竟,您只是为了测试目的而破坏封装。

    【讨论】:

      【解决方案3】:

      我只在不控制需要测试的类时才这样做。 Charles 指出当您控制源代码时,您可以在 .NET 中做什么。

      您的情况有点不同,因为您的类旨在用作超类,因此通过子类对其进行测试很有意义(因为该类不打算独立存在)。在这种情况下,您基本上是在做Test-Specific Subclass 的变体。链接中描述的模式更多地是关于覆盖行为,如果您有一个没有首先编写测试并且具有高耦合的类,那么它非常有用,这是一种梳理依赖关系的好方法(通常作为中间步骤获取一个单独的类作为参数传递)。在您的情况下,“覆盖”是为了增加访问权限,但它似乎符合相同的模式。

      【讨论】:

      • 实际上在阅读了模式的描述之后,我会说 Test-specific subclass 正是我所做的。该模式将“行为暴露子类”描述为特定于测试的子类的示例。我不知道那个网站,但它看起来很棒,我认为在那里我会找到构建更好测试代码的灵感,谢谢! (我已经订购了这本书)
      【解决方案4】:

      这里我想提到另一种模式,因为它是 Test-Specific Subclass 的一个特例。据我所知,它没有自己的名字:让测试用例扩展被测类。示例:

      namespace MySystem.Unittests {
          [TestFixture]
          public class SomeClassTestCase : SomeClass {
              [Test]
              public void TestDoSomething() {
                  // exercise DoSomething()
              }
          }
      }
      

      当然,这只有在 SomeClass 有一个无参数的构造函数时才有效。

      【讨论】:

        猜你喜欢
        • 2019-07-17
        • 2014-06-14
        • 2018-06-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-03-09
        • 2012-11-09
        相关资源
        最近更新 更多