I\'m new to TDD, and I find RegExp quite a particular case. Is there any special way to unit test them, or may I just treat them as regular functions?
I can't believe no one has posted this amazing tool:
It lets you test your regular expressions. You define some text containing strings it should match, and strings it shouldn't match, and if it's all green, you're good. For example here's one I made to match slugs: http://refiddle.com/by/callum-locke/slug-matcher
I always test them just as I do any other function. Make sure they match things you think they should match and that they don't match things they shouldn't.
Just throw a bunch of values at it, checking that you get the right result (whether that's match/no-match or a particular replacement value etc).
Importantly, if there are any corner cases which you wonder whether they'll work or not, capture them in a unit test and explain in a comment why they work. That way someone else who wants to change the regex will be able to check that the corner case still works, and it'll give a hint to them as to how to fix it if it breaks.
I would create a set of input values with expected output values, much like every other test case.
Also, I can thoroughly recommmend the free Regex Tool Expresso. It's a fantastic regex editor/debugger that has saved me days of pain in the past.
I think a simple input ouput test is sufficient. As time goes by and some cases occur in which your regex fails, don't forget to add these cases to the test as well while fixing.
Use a fixture in your unit test library of choice and follow the usual TDD approach:
Here is a sample fixture stub for spock as a test runner:
@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
}
}
It can be run as a stand-alone groovy script like
groovy regex-test.groovy
Disclaimer: The snippet is taken from a series of blog posts I wrote some weeks ago