@DirtiesContext
is a Spring testing annotation.- It tells spring that the Class or test method with this annotation modifies the application context.
- Once
@DirtiesContext
is used, the cached application context will be recreated. @DirtiesContext
is both a class and method level annotation.
The problems
Cost and speed of unit tests
Closing and recreating the application context is costly. Imagine a big project with a lot of classes and tests where we are using @DirtiesContext
a lot. This will greatly slow the testing of the application.
False Negative tests

These are tests that should fail but are successfully running. Consider the below example which demonstrate a transaction error.
// Service interface
public interface ThemeParkRideService {
/**
* Find all rides
*
* @return list of ThemeParkRide
*/
List<ThemeParkRide> getRides();
/**
* Create ride
*
* @param themeParkRide
*/
void createRide(ThemeParkRide themeParkRide);
}
In the service implementation below, in the method createRide(themeParkRide)
, see the part throw new RuntimeException("Example error")
. Here we are simulating a runtime error. The service also do not have an @Transactional
annotation set.
// Service implementation
@RequiredArgsConstructor
@Service
public class ThemeParkRideServiceImpl implements ThemeParkRideService {
private final ThemeParkRideRepository themeParkRideRepository;
@Override
public List<ThemeParkRide> getRides() {
return themeParkRideRepository.findAll();
}
@Override
public void createRide(ThemeParkRide themeParkRide) {
themeParkRideRepository.save(themeParkRide);
throw new RuntimeException("Example error");
}
}
// Test class
@SpringBootTest
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
class ThemeParkRideServiceBadTest {
@Autowired
private ThemeParkRideService service;
@Autowired
private ThemeParkRideRepository repository;
@Test
@Order(1)
@DirtiesContext
void testCreateRide() {
ThemeParkRide ride = new ThemeParkRide("Rollercoaster",
"Train ride",
5,
3);
Assertions.assertThrows(RuntimeException.class, () ->
service.createRide(ride));
}
@Test
@Order(2)
void testGetRides() {
repository.save(
new ThemeParkRide("Teacups",
"Spinning ride",
2,
4));
List<ThemeParkRide> rideList = service.getRides();
assertThat("One ride must be available", rideList, hasSize(1));
assertThat("Name is Teacups", rideList.get(0).getName(), is("Teacups"));
}
}
Here all the tests will run successfully as the testCreateRide()
will get the expected RuntimeException
and the testGetRides()
will get one ThemeParkRide
with name “Teacups”.
These are clearly a false negative tests as the @DirtiesContext
annotation is hiding the transaction problem in the service implementation class.
The solution
Remove @DirtiesContext
from the test method testCreateRide()
.
// Refactored test class
@SpringBootTest
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
class ThemeParkRideServiceBadTest {
@Autowired
private ThemeParkRideService service;
@Autowired
private ThemeParkRideRepository repository;
/**
* Test method with a @DirtiesContext annotation to clean all created data after the test.
* Test is expecting a RuntimeException
*/
@Test
@Order(1)
void testCreateRide() {
ThemeParkRide ride = new ThemeParkRide("Rollercoaster",
"Train ride",
5,
3);
Assertions.assertThrows(RuntimeException.class, () ->
service.createRide(ride));
}
/**
* Test method is checking data fetch
*/
@Test
@Order(2)
void testGetRides() {
repository.save(
new ThemeParkRide("Teacups",
"Spinning ride",
2,
4));
List<ThemeParkRide> rideList = service.getRides();
assertThat("One ride must be available", rideList, hasSize(1));
assertThat("Name is Teacups", rideList.get(0).getName(), is("Teacups"));
}
}
Here only the test testCreateRide()
will run successfully get the expected RuntimeException
however the test testGetRides()
will now fail as it is expecting one ThemeParkRide
with name “Teacups” but instead is getting two rides. One with name “Teacups” and the other with name “Teacups”
The explanation
- Spring rollbacks transactions on
RuntimeException
detection. - Since we did not use an
@Transactional
annotation in our service class,createRide()
method will commit even though it encounters aRuntimeException
. - The test method
testGetRides()
is now failing as the it is getting the committed data fromcreateRide()
also. - This is a correct behavior which alerts the developer that an
@Transactional
should be set in the service class. @DirtiesContext
was silently hiding the transactional problem.
2 Comments
Add Yours →Thank Information
Thank you for the intended article.