6

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

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

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

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

4

4 回答 4

10

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

首先,创建Configuration一个非静态类。谷歌工程师 Miško Hevery 就面向可测试性的 OO 设计做了一个非常好的演讲,他特别提到全局状态是一个糟糕的设计选择,特别是如果你想测试它。

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

现在,好处:

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

如果你觉得你不太擅长依赖注入,我会推荐一个了不起的艺术和工程,由马克西曼的天才提供给凡人的灵魂,称为.NET 中的依赖注入。我读过的关于类设计的最好的书之一,它面向 .NET 开发人员。

为了让我的回答更具体:

我应该使用反射来为每个测试重新构建类吗?

不,你不应该在你的测试中使用反射(只有在没有其他情况的情况下)。反思将使您进行测试:

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

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

我应该重新设计这个类来检查属性而不是构造函数中的注册表吗?

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

现在我没有足够的信息来告诉你是应该通过构造函数读取注册表RegistryProviderConfiguration还是按需懒惰地读取注册表,这是一个棘手的问题。但我绝对可以说 - 尽量避免全局状态,尽量减少对测试中实现细节的依赖(这与整个 OO 原则有关)并尝试单独测试对象,即不访问外部系统。然后你可以模仿特殊情况,例如当注册表不可用时,你的类行为是否符合预期?如果您通过类的静态成员直接访问注册表,则很难重新创建这样的场景Configuration

于 2013-03-31T14:48:00.030 回答
1

众所周知,静态类/方法很难进行单元测试。
(还请注意,您当前所做的根本不是单元测试;它是集成测试(您正在更改测试的注册表值)。)

恐怕你必须在你的类是可测试的还是静态的之间做出选择。

您可以做出的妥协是将“逻辑”位(即验证等)移动到不同的非静态类,该类将由主静态类调用。
然后可以轻松测试该非静态类。

于 2013-03-31T14:48:39.687 回答
0

我会选择重新设计。如果出现任何问题,也有一个 TypeInitializationException 可能会使用户/开发人员感到困惑。

于 2013-03-31T14:39:48.390 回答
-1

我建议调整您的代码以使用单例模式作为第一步。

于 2013-03-31T14:41:14.350 回答