tl;dr

We show how to develop a regular expression driven by automated tests in JUnit 5. Download setup from github.com/JohannesFKnauf/regex-tdd-parameterized-junit5-example and start playing.

git clone https://github.com/JohannesFKnauf/regex-tdd-parameterized-junit5-example
mvn clean test

Regex Testing: Now for JUnit 5

It’s been a year since the article series about

In the meantime, the popularity of JUnit 5 has grown. It’s time to provide sample code.

Indeed, JUnit 5 will make parameterized tests a first-class citizen. It is a new native feature. However, the JUnit 5 User Guide still marks parameterized tests as experimental.

Providing data by @MethodSource

In order to implement the exact same pattern of 1 self-sufficient simple test class, we choose the @MethodSource test data source over more complex alternatives. I recommend to read the JUnit 5 User Guide, Nicolai Parlog’s article or Ali Dehghani’s article on Baeldung, if you want to learn more.

@MethodSource works by reading a Stream of Arguments’ from a static method result. The second annotation we need is @ParameterizedTest, which replaces @Test on a method.

package de.metamorphant.examples;

import static org.junit.jupiter.api.Assertions.assertEquals;

import java.util.stream.Stream;

import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;

public class ParameterizedRegexTest {
  private static final String REGEX = "[-+]?\\d+(\\.\\d+)?([eE][-+]?\\d+)?";

  static Stream<Arguments> testCases() {
    return Stream.of(
        Arguments.of("", false, "empty string"), 
        Arguments.of("a", false, "single non-digit"),
        Arguments.of("1", true, "single digit"), 
        Arguments.of("123", true, "integer"),
        Arguments.of("-123", true, "integer, negative sign"),
        Arguments.of("+123", true, "integer, positive sign"), 
        Arguments.of("123.12", true, "float"),
        Arguments.of("123.12e", false, "float with exponent extension but no value"),
        Arguments.of("123.12e12", true, "float with exponent"),
        Arguments.of("123.12E12", true, "float with uppercase exponent"),
        Arguments.of("123.12e12.12", false, "float with non-integer exponent"),
        Arguments.of("123.12e+12", true, "float with exponent, positive sign"),
        Arguments.of("123.12e-12", true, "float with exponent, negative sign")
      );
  }

  @ParameterizedTest(name = "{index} ==> {2}: is {0} well-formed? {1}")
  @MethodSource("testCases")
  public void regexTest(String input, boolean isMatchExpected, String description) {
    Boolean matches = input.matches(REGEX);
    assertEquals(isMatchExpected, matches);
  }
}

That’s all. Pretty simple, isn’t it? It stays ugly Java code, of course.

Build setup

Of course, we still need to set up the proper dependencies.

<project xmlns="http://maven.apache.org/POM/4.0.0"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>de.metamorphant.examples</groupId>
  <artifactId>junit5-parameterized-regex-testing-demo</artifactId>
  <version>0.0.1-SNAPSHOT</version>

  <properties>
    <maven.compiler.target>1.8</maven.compiler.target>
    <maven.compiler.source>1.8</maven.compiler.source>
  </properties>

  <dependencies>
    <dependency>
      <groupId>org.junit.jupiter</groupId>
      <artifactId>junit-jupiter-api</artifactId>
      <version>5.6.1</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.junit.jupiter</groupId>
      <artifactId>junit-jupiter-engine</artifactId>
      <version>5.6.1</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.junit.jupiter</groupId>
      <artifactId>junit-jupiter-params</artifactId>
      <version>5.6.1</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
  <build>
    <plugins>
      <plugin>
        <artifactId>maven-surefire-plugin</artifactId>
        <version>2.22.2</version>
      </plugin>
    </plugins>
  </build>
</project>

A few notes:

  • org.junit.jupiter:junit-jupiter-params is the only difference to an ordinary JUnit 5 project. This artifact contains the parameterized test features.
  • Pinning the surefire plugin to a recent version will already be familiar to you as a JUnit 5 user.
  • Selecting Java 8 (or above) is needed to use the Arguments.of(...) static interface methods.

Run and confirm: It’s green.

mvn clean test


Post header background image by WikimediaImages from Pixabay.


Contact us