Programming
Prevent unit tests but allow integration tests in Maven
In the world of Java development with Maven, managing different types of tests effectively is paramount for maintaining a robust and efficient build pipeline. There are often scenarios where you need to prevent unit tests but allow integration tests in Maven, particularly when optimizing build times for continuous integration (CI) environments or focusing on specific test phases. This targeted approach ensures that your development process remains agile, providing quicker feedback loops without sacrificing the thoroughness of your integration testing. Understanding how to precisely control Maven’s test execution using its powerful plugin ecosystem is a key skill for any developer aiming for a streamlined software development lifecycle.
Why Separate Unit and Integration Tests?
The distinction between unit tests and integration tests is fundamental to modern software quality assurance. Unit tests are designed to verify small, isolated pieces of code—individual methods or classes—in isolation from external dependencies. They are typically fast, numerous, and provide immediate feedback on code changes. Conversely, integration tests validate the interactions between different components or systems, often involving databases, external APIs, or other services. These tests are inherently slower and more complex but are crucial for catching issues that arise from component interactions.
Separating these tests allows for more granular control over the build process. When you prevent unit tests but allow integration tests in Maven, you’re making a conscious decision about what level of testing is appropriate for a given build stage. For instance, in a rapid development cycle, you might run only unit tests locally for quick verification. However, before deploying to a staging environment, you’d want to execute integration tests to ensure all components work together as expected, without the overhead of running potentially thousands of unit tests again.
This strategic separation significantly impacts the development workflow. Running only integration tests can be beneficial for specific CI/CD stages where the focus shifts from individual component correctness to system-wide functionality. As noted by industry experts, “Efficient testing strategies involve a multi-layered approach, ensuring that the right tests run at the right time, minimizing bottlenecks while maximizing coverage.”
Maven’s Solution: Surefire vs. Failsafe
Maven provides two distinct plugins for handling different types of tests: the Surefire Plugin and the Failsafe Plugin. While both are capable of executing tests, their default behaviors and typical configurations are tailored for specific purposes within the Maven build lifecycle. Understanding their roles is crucial when you aim to prevent unit tests but allow integration tests in Maven.
Surefire for Unit Tests
The Maven Surefire Plugin is the default choice for running unit tests. By convention, it executes tests whose names follow patterns like Test.java or Test.java, typically during the test phase of the Maven build lifecycle. Surefire is designed for speed and isolation, making it ideal for fast, frequent checks of individual code units. It aggregates test reports and failures, providing quick feedback on the health of your codebase.
Failsafe for Integration Tests
The Maven Failsafe Plugin, on the other hand, is specifically designed for running integration tests. It binds to the pre-integration-test and post-integration-test phases of the Maven lifecycle. Failsafe looks for test classes named IT.java (by default) or other patterns you configure. Its key advantage is that it doesn’t fail the build immediately if tests fail; instead, it waits until the verify phase to report failures. This allows setup and teardown phases to complete, even if an integration test fails, which is often critical for cleaning up resources after complex integration scenarios. You can find comprehensive documentation for the Failsafe Plugin on the official Apache Maven website.
Configuring Maven to Prevent Unit Tests
To effectively prevent unit tests but allow integration tests in Maven, you need to configure both the Surefire and Failsafe plugins within your pom.xml. The strategy involves skipping the Surefire plugin’s execution entirely, while ensuring the Failsafe plugin is correctly configured to run your integration tests. This approach gives you fine-grained control over which test types are executed during a given Maven build.
The Surefire Exclusion Strategy
The simplest way to skip unit tests executed by the Surefire plugin is to set the skipTests property to true. This can be done directly in your pom.xml or via the command line. For instance, if you want to run your build without any unit tests, you would execute mvn verify -DskipTests. However, for a more permanent configuration within the pom.xml for specific profiles or default behavior, you can configure the Surefire plugin as follows:
<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <version>3.2.5</version> <!-- Use a recent stable version --> <configuration> <skipTests>true</skipTests> </configuration> </plugin> <!-- ... other plugins ... --> </plugins> </build>
This configuration ensures that Surefire is skipped by default. For more detailed Surefire configurations, including how to exclude specific test classes or packages, refer to the Maven Surefire Plugin documentation.
Ensuring Failsafe Runs
While skipping Surefire prevents unit tests, you must explicitly configure Failsafe Question & Answer :
I’ve a Maven build in which I use the SureFire plugin to run some unit tests, and the FailSafe plugin to run some integration tests. I would like a way to run just the FailSafe plugin’s tests.
It’s not a good solution for me to add different profiles or anything in the pom, because it’s a multimodule build and I don’t want to have to edit every module’s pom.
There are skip.tests and maven.test.skip and skipTests which stop all tests, and skipITs, which stops only the failsafe plugin.
So, is there a command-line flag for Maven like skipITs, but instead with the functionality of “onlyITs”?
I found the simplest way to skip only surefire tests is to configure surefire (but not failsafe) as follows:
<plugin> <artifactId>maven-surefire-plugin</artifactId> <version>2.14</version> <configuration> <!-- skips surefire tests without skipping failsafe tests. Property value seems to magically default to false --> <skipTests>${skip.surefire.tests}</skipTests> </configuration> </plugin>
This allows you to run mvn verify -Dskip.surefire.tests and only surefire, not failsafe, tests will be skipped; it will also run all other necessary phases including pre-integration and post-integration, and will also run the verify goal which is required to actually fail your maven build if your integration tests fail.
Note that this redefines the property used to specify that tests should be skipped, so if you supply the canonical -DskipTests=true, surefire will ignore it but failsafe will respect it, which may be unexpected, especially if you have existing builds/users specifying that flag already. A simple workaround seems to be to default skip.surefire.tests to the value of skipTests in your <properties> section of the pom:
<properties> <skip.surefire.tests>${skipTests}</skip.surefire.tests> </properties>
If you need to, you could provide an analagous parameter called skip.failsafe.tests for failsafe, however I haven’t found it necessary - because unit tests usually run in an earlier phase, and if I want to run unit tests but not integration tests, I would run the test phase instead of the verify phase. Your experiences may vary!
These skip.(surefire|failsafe).tests properties should probably be integrated into surefire/failsafe code itself, but I’m not sure how much it would violate the “they’re exactly the same plugin except for 1 tiny difference” ethos.