c# - 设计问题 : static class only initializes once, 破坏了单元测试

标签 c# unit-testing nunit static-constructor

我有一个静态Configuration 类负责我整个系统的数据设置。它在其构造函数中从注册表加载某些值,并且其所有方法都基于这些值。如果它无法从注册表中获取值(如果应用程序尚未激活则可能),它会抛出异常,转换为 TypeInitializationException,这对我来说没问题。

我使用 NUnit 编写了单元测试,以确保 Configuration 的构造函数正确处理所有情况 - 正常值、空白值、Null 值。每个测试使用相关值初始化注册表,然后调用 Configuration 中的一些方法。 这就是问题所在:NUnit 决定首先运行 Null 测试。它清除注册表、初始化配置、抛出异常——一切都很好。但是,因为这是一个静态类,其构造函数刚刚失败 - 它不会为其他测试再次重新构造该类,并且它们都失败了。

即使没有 Null 测试,我也会遇到问题,因为配置可能(我猜)会为所有使用它的类初始化一次。

我的问题是:我应该使用反射为每个测试重新构造类,还是应该重新设计此类以检查属性而不是构造函数中的注册表?

最佳答案

我的建议是稍微重新设计您的 Configuration 类。因为你的问题本质上是理论性的,有一点实践方面(单元测试失败),我会提供一些链接来支持我的想法。

首先,使Configuration 成为一个非静态类。谷歌工程师 Miško Hevery 关于 OO Design for Testability 的演讲非常精彩他特别提到全局状态是一个糟糕的设计选择,特别是如果你想测试它的话。

您的配置可以通过构造函数接受 RegistryProvider 实例(我假设您听说过依赖注入(inject)原则)。 RegistryProvider 的责任只是从注册表中读取值,这是它唯一应该做的事情。现在,当您测试 Configuration 时,您将提供 RegistryProvider stub (如果您不知道什么是 stub 和模拟 - google 一下,它们本质上很简单),您可以在其中将硬编码特定注册表项的值。

现在,好处:

  • 你有很好的单元测试,因为你不依赖注册表
  • 你没有全局状态(可测试性)
  • 你的测试不依赖于 彼此(每个都有单独的 Configuration 实例)
  • 您的测试不依赖于执行它们的环境(您可能没有访问注册表的权限,但您仍然可以测试您的Configuration 类)

如果您觉得自己不太擅长依赖注入(inject),我会推荐一个奇妙的艺术和工程作品,由 Mark Seemann 的天才提供给凡人的灵魂,称为 Dependency Injection in .NET .我读过的关于类设计的最好的书之一,它面向 .NET 开发人员。

为了让我的回答更具体:

Should I use reflection to re-construct the class for each test?

不,你永远不应该在你的测试中使用反射(只有在没有其他情况的情况下)。反射会让你测试:

  • 脆弱的
  • 难以理解
  • 难以维护

使用面向对象的实践结合封装来实现实现的隐藏。然后只测试外部行为,不要依赖内部实现细节。这将使您的测试仅依赖于外部行为而不依赖于内部实现,后者可能会发生很大变化。

should I re-design this class to check the registry in a property rather than the constructor?

按照我的回答中的描述设计你的类(class)将使你能够测试你的类(class)而不访问注册表。这是单元测试的基石——不依赖外部系统(数据库、文件系统、网络、注册表 等...)。如果您想测试您是否可以访问注册表 - 编写单独的集成测试,您将在其中写入注册表并从中读取。

现在我没有足够的信息来告诉你是应该通过 Configuration 构造函数中的 RegistryProvider 读取注册表,还是按需延迟读取注册表,这是一个棘手的问题.但我绝对可以说 - 尽量避免全局状态,尽量减少对测试中实现细节的依赖(这与整个 OO 原则相关)并尝试隔离测试对象,即不访问外部系统。然后您可以模拟异常情况,例如,当注册表不可用时,您的类是否按预期行为?如果您直接通过 Configuration 类的静态成员访问注册表,则很难重现这种情况。

关于c# - 设计问题 : static class only initializes once, 破坏了单元测试,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15730509/

相关文章:

.net - NUnit 多个 TestFixture 等效于 MsTest

c# - 在类中并行运行 NUnit 测试但不与其他类测试一起运行的方法?

c# - Automapper:映射由基类对象组成的集合

c# - 我在 Visual Studio 2017 上找不到跨平台应用程序 Xamarin

unit-testing - 单元测试和模拟 Node JS 网络/套接字事件

java - 我们可以模拟第三方对象的创建或调用吗?

asp.net-mvc - 使用 Moq 通过存储库返回集合

c# - 如何在 wpf MVVM 中选择文本框的文本

c# - 将 json 值转换为缺少某些值的数据表

java - 在 Java 中使用泛型为类编写什么单元测试?