我正在寻找一些关于单元测试的建议,不会在生产代码中留下“测试挂钩”。
假设我有一个名为MethodLogger的静态类(VB.NET模块),它有一个“void WriteEntry(string Message)”方法,用于将调用方法和消息写入磁盘上的日志文件。 WriteEntry()的实际目标可以转移到IMethodLogger的虚假实现以运行单元测试,对于所有其他情况,它应该调用IMethodLogger的实际实现。
这是我到目前为止所提出的内容的粗略描述,我喜欢这种方法 - 但在一些快速测试中,它提出了一些问题和怀疑:
[InternalAttributesVisibleTo("MethodLogger.Tests")]
internal static class MethodLogger
{
public void WriteEntry(string message)
{
LogWriter.WriteEntry(message);
}
private IMethodLogger LogWriter
{
get;
set;
}
#if DEBUG
internal void SetLogWriter(IMethodLogger logger)
{
LogWriter = logger;
}
#endif
}
以下是我的具体问题:
这使得单元测试与DEBUG构建的运行紧密结合;当我运行单元测试时虽然看起来它没有专门重建DEBUG中的测试组件 - 是否可以这样做?
我可以使用像“TEST”这样的自定义预编译器标志代替“DEBUG”吗?我如何告诉Visual Studio始终使用这个标志重建目标以进行测试以确保我的钩子/接缝可用?
答案 0 :(得分:4)
您可以使用Typemock Isolator来规避任何损害可测试性的设计问题。 与其他框架不同,它还可以使用静态方法,私有方法和非虚方法。 当你学会更好地设计时,你的代码默认会更“可测试”, 但如果你现在想解决这些问题,这将有所帮助
http://blog.typemock.com/2009/01/isolator-new-vbnet-friendly-api.html
(另外,它是唯一具有VB友好API的工具)
答案 1 :(得分:3)
我们确保针对Release / Obfuscated版本运行测试,因为我们经常以这种方式发现问题,所以,是的,我认为让它们工作是有价值的。
混淆会使内部构件完全无法使用,这对我来说已经足够了,所以我们留下测试方法(并将它们放在内部)。
总的来说,我宁愿让代码进行测试而不是担心它们存在 - 只要它们不会影响性能。
答案 2 :(得分:3)
我不喜欢不能亲自针对真实的生产二进制文件运行测试的想法。
你能不能将setter标记为“过时”并禁用测试代码中的过时警告?除了合适的文档之外,还应该阻止生产代码意外调用它,但仍然可以将其用于测试。
但是,要解决您的实际问题:
答案 3 :(得分:3)
实际上,你正在走向依赖注入 - 你还没有实现它:)
将接缝留在原位没有任何问题 - 您只需要对它们进行建模,以便它们不是特定于测试,而是实际上是API的增强功能。这需要一些练习,但首先要改变思维方式。
我不久前在Testability Is Really The Open/Closed Principle写了这篇文章。
在您的特定情况下,您应该对MethodLogger类进行一些更改:
答案 4 :(得分:2)
拥有#IF DEBUG
等......是不好的形式。
生产代码应独立于测试/调试代码。
我建议研究依赖注入以帮助进行单元测试。
答案 5 :(得分:2)
根据Michael Feathers的“有效使用遗留代码”一书,如果你将setter命名为特定于测试的东西,那么它似乎会更好。
实施例: SetTestingLogger(IMethodLogger记录器)
我也会删除预处理器语句。这本书还说明并引用
“你可能会有点不安 您要删除访问权限的想法 使用静态setter时保护, 但要记住的目的 访问保护是为了防止 错误。我们正在进行测试 也防止错误。“
<强>结论:强> 看起来最好放置这些钩子,因为你出于测试原因放置它们并最终消除错误。
书中的“介绍静态设定器”部分更详细地解释了这一点。我会推荐它作为每个开发人员应该拥有的书。
答案 6 :(得分:2)
答案 7 :(得分:1)
这是一种内部方法。只需保留原样,无需条件编译,只需从单元测试中调用即可。
离开钩子没有任何问题 - 事实上,您甚至可能想要考虑使用 public 方法并允许任何代码更改日志记录钩子。
再次查看您的代码;你甚至不需要 SetLogWriter方法;只需为静态类使用私有访问器,您就可以在单元测试中自行设置。
我可以看到将钩子留在原位是可以的,删除它只是一个偏好,但我不同意将其公开 - 感觉太像提升权限只是因为它删除了小麻烦。 - Yoooder
好吧,我更多地评论一般代码组织原则。为代码创建这样的钩子通常是个好主意;在您的情况下,您将不会“提升权限以消除麻烦”,您将“设计一个强大而灵活的日志框架”。
答案 8 :(得分:1)
对我来说,这似乎是IoC容器的主要候选者。我会让我的记录器不是静态的,并为测试,开发和生产设置不同的配置。
答案 9 :(得分:1)
您的构建过程必须包含一个生成版本,并对其执行单元测试。如果没有,你不能保证有人没有使用pragma(例如发布与调试)。
在记录的情况下,我总是使用Log4Net。在开发和测试期间,日志消息设置在最详细的级别,在生产中我倾向于将它们保留在错误或信息级别,但在必要时设置为更详细的故障排除。
IMO是使用SetLogWriter执行您要完成的任务的最佳方法实际上是使用控件容器的反转,如StructureMap,根据各种配置在运行时分配混凝土。在单元测试期间,您可以使用像Moq这样的库来制造以预期方式运行的混凝土(SQL查询始终返回相同的精确结果),但在您的特定情况下,您执行相反的操作(所需的日志记录)在单元测试期间与生产运行时没有记录)。
答案 10 :(得分:0)
我可以使用 自定义预编译器标志,如“TEST” 代替“调查”?我怎么去 关于告诉Visual Studio永远 使用此标志重建目标 测试以确保我的钩子/接缝 可用?
是。您要做的是添加条件编译符号。您可以在“构建”选项卡下的项目属性中执行此操作。您可以在Configuration Manager中创建不同的构建配置,并仅将TEST符号添加到新配置中。