【发布时间】:2010-10-04 01:37:53
【问题描述】:
我是 TDD 的新手,我发现 RegExp 是一个非常特殊的案例。有什么特殊的方法可以对它们进行单元测试,或者我可以将它们视为常规函数吗?
【问题讨论】:
标签: regex unit-testing tdd
我是 TDD 的新手,我发现 RegExp 是一个非常特殊的案例。有什么特殊的方法可以对它们进行单元测试,或者我可以将它们视为常规函数吗?
【问题讨论】:
标签: regex unit-testing tdd
您应该始终测试您的正则表达式,就像任何其他代码块一样。它们最多是一个接受字符串并返回布尔值或返回值数组的函数。
以下是一些关于在为 regexen 设计单元测试时应该考虑的事项的一些建议。这些不是单元测试设计的硬性规定,而是一些指导你思考的指导方针。与往常一样,权衡您的测试需求与失败成本以及实施它们所需的时间。 (我发现“实施”测试很容易!:-])
需要考虑的要点:
对于返回列表的正则表达式,还要记住:
(?<name> thing1 ( thing2) )) - 根据您使用的正则表达式引擎,此行为可能会有所不同。如果您使用任何高级功能,例如非回溯组,请确保您完全了解该功能的工作原理,并使用上述指南,构建适用于和反对每个功能的示例字符串。
根据您的正则表达式库实现,捕获组的方式也可能不同。 Perl 5 有一个'open paren order' 排序,C# 有一部分,除了命名组等等。请务必尝试您的口味,以确切了解它的作用。
然后,将它们与您的其他单元测试直接集成到它们自己的模块中或与包含正则表达式的模块一起。对于特别讨厌的正则表达式,您可能会发现需要进行大量测试来验证模式和您使用的所有功能是否正确。如果正则表达式构成了该方法所做的大部分(或几乎所有)工作,我将使用上面的建议来设计输入以测试该函数,而不是直接测试正则表达式。这样,如果稍后您决定不适合使用正则表达式,或者您想将其分解,您可以在不更改接口的情况下捕获正则表达式提供的行为 - 即调用正则表达式的方法。
只要您真正了解正则表达式功能应该如何以您的正则表达式风格工作,您就应该能够为它开发体面的测试用例。只要确保您真的、真的、真的了解该功能的工作原理!
【讨论】:
只需向它抛出一堆值,检查您是否得到正确的结果(无论是匹配/不匹配还是特定的替换值等)。
重要的是,如果您想有任何极端情况它们是否会起作用,请在单元测试中捕获它们并在评论中解释为什么它们会起作用.这样,想要更改正则表达式的其他人将能够检查极端情况是否仍然有效,并且会提示他们如何修复它。
【讨论】:
大概你的正则表达式包含在一个类的方法中。例如:
public bool ValidateEmailAddress( string emailAddr )
{
// Validate the email address using regular expression.
return RegExProvider.Match( this.ValidEmailRegEx, emailAddr );
}
您现在可以为此方法编写测试。我想关键是正则表达式是一个实现细节——您的测试需要测试接口,在这种情况下,它只是验证电子邮件方法。
【讨论】:
我会创建一组具有预期输出值的输入值,就像所有其他测试用例一样。
另外,我可以彻底推荐免费的正则表达式工具Expresso。 这是一个很棒的正则表达式编辑器/调试器,让我在过去免了几天的痛苦。
【讨论】:
我总是像测试其他功能一样测试它们。确保它们与您认为它们应该匹配的东西匹配,并且它们不匹配它们不应该匹配的东西。
【讨论】:
我喜欢针对相反的正则表达式测试该正则表达式,我将针对可能的测试执行这两项操作,并确保交集为空。
【讨论】:
考虑首先编写测试,并且只编写通过每个测试所需的正则表达式。如果您需要扩展您的正则表达式,请通过添加失败测试来实现。
【讨论】:
我认为一个简单的输入输出测试就足够了。随着时间的推移,在某些情况下您的正则表达式会失败,请不要忘记在修复时将这些情况也添加到测试中。
【讨论】:
在您选择的单元测试库中使用夹具并遵循通常的 TDD 方法:
这是 spock 作为测试运行程序的示例夹具存根:
@Grab('org.spockframework:spock-core:1.3-groovy-2.5')
@GrabExclude('org.codehaus.groovy:groovy-nio')
@GrabExclude('org.codehaus.groovy:groovy-macro')
@GrabExclude('org.codehaus.groovy:groovy-sql')
@GrabExclude('org.codehaus.groovy:groovy-xml')
import spock.lang.Unroll
class RegexSpec extends spock.lang.Specification {
String REGEX = /[-+]?\d+(\.\d+)?([eE][-+]?\d+)?/
@Unroll
def 'matching example #example for case "#description" should yield #isMatchExpected'(String description, String example, Boolean isMatchExpected) {
expect:
isMatchExpected == (example ==~ REGEX)
where:
description | example || isMatchExpected
"empty string" | "" || false
"single non-digit" | "a" || false
"single digit" | "1" || true
"integer" | "123" || true
"integer, negative sign" | "-123" || true
"integer, positive sign" | "+123" || true
"float" | "123.12" || true
"float with exponent extension but no value" | "123.12e" || false
"float with exponent" | "123.12e12" || true
"float with uppercase exponent" | "123.12E12" || true
"float with non-integer exponent" | "123.12e12.12" || false
"float with exponent, positive sign" | "123.12e+12" || true
"float with exponent, negative sign" | "123.12e-12" || true
}
}
它可以像一个独立的 groovy 脚本一样运行
groovy regex-test.groovy
免责声明:sn-p 摘自我几周前写的一系列博客文章
【讨论】: