In JUnit 5, you can use the @Tag annotation to group tests into categories such as:
fastslowunitintegrationdatabaseapismoke
Tags are useful when you want to run only certain groups of tests, for example only fast unit tests during development, or only integration tests in a CI pipeline.
1. Basic Example
Use @Tag on a test method:
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
class CalculatorTest {
@Test
@Tag("fast")
void shouldAddTwoNumbers() {
Calculator calculator = new Calculator();
int result = calculator.add(2, 3);
assertEquals(5, result);
}
@Test
@Tag("slow")
void shouldCalculateLargeDataSet() {
Calculator calculator = new Calculator();
int result = calculator.processLargeDataSet();
assertEquals(1000, result);
}
}
In this example:
shouldAddTwoNumbers()belongs to thefastgroup.shouldCalculateLargeDataSet()belongs to theslowgroup.
2. Tag an Entire Test Class
You can place @Tag on a class to apply the tag to all tests inside it.
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertTrue;
@Tag("unit")
class StringUtilsTest {
@Test
void shouldReturnTrueForBlankString() {
assertTrue(StringUtils.isBlank(""));
}
@Test
void shouldReturnTrueForWhitespaceString() {
assertTrue(StringUtils.isBlank(" "));
}
}
All tests in StringUtilsTest are now tagged as unit.
3. Use Multiple Tags
A test can have more than one tag.
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertNotNull;
class UserServiceTest {
@Test
@Tag("unit")
@Tag("fast")
void shouldCreateUser() {
UserService userService = new UserService();
User user = userService.createUser("[email protected]");
assertNotNull(user);
}
@Test
@Tag("integration")
@Tag("database")
void shouldSaveUserToDatabase() {
UserRepository userRepository = new UserRepository();
User user = userRepository.save(new User("[email protected]"));
assertNotNull(user.getId());
}
}
This allows you to run tests by different categories.
For example:
- Run all
unittests. - Run all
fasttests. - Run all
integrationtests. - Exclude all
databasetests.
4. Run Tagged Tests with Maven
If you are using Maven Surefire, you can run tests with a specific tag like this:
mvn test -Dgroups=unit
To run multiple tags:
mvn test -Dgroups=unit,fast
To exclude a tag:
mvn test -DexcludedGroups=slow
Example:
mvn test -Dgroups=integration -DexcludedGroups=slow
This runs tests tagged with integration, but excludes tests tagged with slow.
5. Configure Tags in pom.xml
You can also configure Maven to include or exclude tags permanently.
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.5.2</version>
<configuration>
<groups>unit</groups>
<excludedGroups>slow</excludedGroups>
</configuration>
</plugin>
</plugins>
</build>
This configuration runs tests tagged unit and excludes tests tagged slow.
6. Run Tagged Tests with Gradle
If you are using Gradle, configure the test task:
test {
useJUnitPlatform {
includeTags 'unit'
excludeTags 'slow'
}
}
Then run:
gradle test
You can also create separate test tasks:
tasks.register('integrationTest', Test) {
useJUnitPlatform {
includeTags 'integration'
}
}
Run it with:
gradle integrationTest
7. Tag Integration Tests
A common use case is separating unit tests from integration tests.
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertTrue;
@Tag("integration")
class UserRepositoryTest {
@Test
void shouldConnectToDatabase() {
boolean connected = true;
assertTrue(connected);
}
}
Then you can run only integration tests:
mvn test -Dgroups=integration
Or exclude them during regular builds:
mvn test -DexcludedGroups=integration
8. Tag Nested Tests
Tags also work with nested test classes.
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
class OrderServiceTest {
@Nested
@Tag("validation")
class ValidationTests {
@Test
void shouldRejectInvalidOrder() {
// validation test
}
@Test
void shouldAcceptValidOrder() {
// validation test
}
}
@Nested
@Tag("pricing")
class PricingTests {
@Test
void shouldCalculateDiscount() {
// pricing test
}
@Test
void shouldApplyTax() {
// pricing test
}
}
}
Here:
- All tests in
ValidationTestsare taggedvalidation. - All tests in
PricingTestsare taggedpricing.
9. Tag Naming Rules
JUnit tag names must follow a few rules:
- A tag must not be blank.
- A tag must not contain whitespace.
- A tag must not contain ISO control characters.
- A tag must not contain reserved characters:
,()&|!
Good examples:
@Tag("unit")
@Tag("integration")
@Tag("fast")
@Tag("database")
@Tag("smoke")
Avoid:
@Tag("unit test")
@Tag("fast,unit")
@Tag("integration&database")
10. Best Practices
Use tags for broad execution groups, not for every small detail.
Good tag categories:
unitintegrationslowfastdatabaseapismoke
Avoid over-tagging tests with too many labels.
For example, this is usually enough:
@Test
@Tag("integration")
@Tag("database")
void shouldSaveCustomer() {
}
But this is probably too much:
@Test
@Tag("integration")
@Tag("database")
@Tag("repository")
@Tag("customer")
@Tag("save")
@Tag("positive")
void shouldSaveCustomer() {
}
Summary
Use JUnit 5 @Tag to group and filter tests.
@Test
@Tag("fast")
void shouldRunQuickly() {
}
You can apply tags to:
- Individual test methods
- Entire test classes
- Nested test classes
Then run selected groups using Maven or Gradle:
mvn test -Dgroups=fast
test {
useJUnitPlatform {
includeTags 'fast'
}
}
Tags are especially useful for separating unit tests, integration tests, slow tests, and database-dependent tests.
