JUnit @TestFactory Annotation

JUnit is a popular testing framework in the Java ecosystem that simplifies writing and running tests. The @TestFactory annotation in JUnit 5 is used to create dynamic tests. This guide covers the basics of using the @TestFactory annotation to generate dynamic tests in JUnit.

Table of Contents

  1. Introduction
  2. Steps to Create Dynamic Tests with @TestFactory
  3. Real-World Use Case
  4. Conclusion

Introduction

The @TestFactory annotation marks methods that are test factories, which are responsible for generating dynamic tests at runtime. This is useful for creating tests dynamically based on various inputs or conditions.

Steps to Create Dynamic Tests with @TestFactory

Step 1: Add Maven Dependency

To use JUnit in your project, you need to add the JUnit dependency to your pom.xml file. Use the latest version of JUnit 5:

<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter-engine</artifactId>
    <version>5.9.2</version>
    <scope>test</scope>
</dependency>

Step 2: Create the Class to be Tested

Create a Java class with methods that you want to test. For example, a simple MathUtils class:

public class MathUtils {
    public int add(int a, int b) {
        return a + b;
    }

    public int subtract(int a, int b) {
        return a - b;
    }
}

Step 3: Create the Dynamic Test Factory

Create a test class in the src/test/java directory. Use the @TestFactory annotation to create a method that generates dynamic tests.

import org.junit.jupiter.api.DynamicTest;
import org.junit.jupiter.api.TestFactory;

import java.util.Arrays;
import java.util.Collection;
import java.util.stream.Stream;

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

public class MathUtilsTest {
    private final MathUtils mathUtils = new MathUtils();

    @TestFactory
    Collection<DynamicTest> dynamicTestsWithCollection() {
        return Arrays.asList(
            dynamicTest("1st dynamic test", () -> assertEquals(2, mathUtils.add(1, 1))),
            dynamicTest("2nd dynamic test", () -> assertEquals(3, mathUtils.add(1, 2))),
            dynamicTest("3rd dynamic test", () -> assertEquals(4, mathUtils.add(2, 2)))
        );
    }

    @TestFactory
    Stream<DynamicTest> dynamicTestsWithStream() {
        return Stream.of(
            dynamicTest("1st dynamic test", () -> assertEquals(2, mathUtils.add(1, 1))),
            dynamicTest("2nd dynamic test", () -> assertEquals(3, mathUtils.add(1, 2))),
            dynamicTest("3rd dynamic test", () -> assertEquals(4, mathUtils.add(2, 2)))
        );
    }
}

In this example, two methods are annotated with @TestFactory. One returns a Collection<DynamicTest>, and the other returns a Stream<DynamicTest>. Each DynamicTest is created using the dynamicTest method, which takes a display name and an executable that contains the test logic.

Step 4: Run the Test

You can run the test using your IDE, Maven, or Gradle.

Using an IDE:

Most IDEs, like IntelliJ IDEA and Eclipse, have built-in support for running JUnit tests. Simply right-click on your test class or method and select “Run.”

Using Maven:

If you’re using Maven, you can run your tests with the following command:

mvn test

Using Gradle:

If you’re using Gradle, you can run your tests with the following command:

gradle test

Real-World Use Case

In real-world applications, dynamic tests are useful for scenarios where the number of tests or the test data is not known in advance. For example, testing a StringUtils class that checks if strings match a certain pattern.

Create the Class to be Tested

Create a Java class that contains business logic. For example, a StringUtils class that checks if strings match a pattern:

import java.util.regex.Pattern;

public class StringUtils {
    private final Pattern pattern;

    public StringUtils(String regex) {
        this.pattern = Pattern.compile(regex);
    }

    public boolean matches(String str) {
        if (str == null) {
            return false;
        }
        return pattern.matcher(str).matches();
    }
}

In this class, the matches method checks if a given string matches a specified pattern.

Create the Dynamic Test Factory

Create a test class for the StringUtils in the src/test/java directory. Use the @TestFactory annotation to create a method that generates dynamic tests based on various input strings and expected results:

import org.junit.jupiter.api.DynamicTest;
import org.junit.jupiter.api.TestFactory;

import java.util.Arrays;
import java.util.Collection;

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

public class StringUtilsTest {
    private final StringUtils stringUtils = new StringUtils("^[a-zA-Z]+$");

    @TestFactory
    Collection<DynamicTest> dynamicTestsForPatternMatching() {
        return Arrays.asList(
            dynamicTest("Test with 'hello'", () -> assertEquals(true, stringUtils.matches("hello"))),
            dynamicTest("Test with 'world'", () -> assertEquals(true, stringUtils.matches("world"))),
            dynamicTest("Test with '1234'", () -> assertEquals(false, stringUtils.matches("1234"))),
            dynamicTest("Test with 'hello123'", () -> assertEquals(false, stringUtils.matches("hello123"))),
            dynamicTest("Test with ''", () -> assertEquals(false, stringUtils.matches("")))
        );
    }
}

In this test class, the dynamicTestsForPatternMatching method is annotated with @TestFactory and returns a Collection<DynamicTest>. Each DynamicTest checks if a given string matches the specified pattern.

Running the Tests

You can run the tests using your IDE, Maven, or Gradle.

Using an IDE:

Most IDEs, like IntelliJ IDEA and Eclipse, have built-in support for running JUnit tests. Simply right-click on your test class or method and select “Run.”

Using Maven:

If you’re using Maven, you can run your tests with the following command:

mvn test

Using Gradle:

If you’re using Gradle, you can run your tests with the following command:

gradle test

Conclusion

The @TestFactory annotation in JUnit makes it easy to generate dynamic tests at runtime. By using @TestFactory, you can create flexible and dynamic tests based on various inputs or conditions without writing multiple test cases.

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top