In JUnit 5, you can use the @Tag annotation to group tests into categories such as:
fast
slow
unit
integration
database
api
smoke
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 the fast group.
shouldCalculateLargeDataSet() belongs to the slow group.
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
unit tests.
- Run all
fast tests.
- Run all
integration tests.
- Exclude all
database tests.
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
ValidationTests are tagged validation.
- All tests in
PricingTests are tagged pricing.
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:
unit
integration
slow
fast
database
api
smoke
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.