While testing REST endpoints using MockMVC, the Spring SecurityContextHolder occasionally returns the wrong username within the same test. I have a service, in which there is a method that returns the username and a JPA repository checks whether the user exists (paraphrased):
String username = SecurityContextHolder.getContext().getAuthentication().getName();
Optional<RemoteUser> foundUser = userRepository.findOneByUsername(username);
Tests are annotated with @WithMockUser(username = "..." roles="...")
when necessary. Each test is torn down afterwards and the Spring application context is refreshed.
@RunWith(SpringRunner.class)
@SpringBootTest(classes = IntegrationTestApplication.class)
public abstract class IntegrationTest {
...
@Resource
private WebApplicationContext applicationContext;
protected MockMvc mockMvc;
...
@Before
public void setUp() {
mockMvc = MockMvcBuilders.webAppContextSetup(applicationContext).build();
}
...
}
About 97-98% of the time, the tests run fine. However, sometimes, the username returned by the Authentication object is not the one that was defined in the @WithMockUser
annotation. Using log statements, I have even seen usernames used in other test classes that have already run. Before each test, database users are set up, so if a username is returned that is not in the database, the test will fail depending on the case for which the user is needed.
Even stranger is that this method in this service can be called multiple times over the course of the test and sometimes the username is correct, and then it all of a sudden is wrong. I do not understand how this is possible. My understanding was that the SecurityContextHolder bean was thread-safe, and therefore the flakiness of the tests baffles me. How can this be happening?
Just a few other things to note:
- Using Spring Boot 2.2.7
- The tests are not run in parallel.
- The aforementioned service is always lazily injected via field injection (
@Lazy
). I don't know if this is an important detail, but this is not my code, and this is the only service that uses this annotation and why this is done this way is unbeknownst to me. - There are a few methods annotated with
@Async
and I thought maybe there could be side effects, but I have nothing to support that and that line of thinking is pure spitballing. - I have debugged the TestSecurityContextHolder and have seen that the methods that clear the context are indeed called.
- I did see this SecurityContextHolder gives wrong User details as well, but one, there is no answer to the question, and two, there are no concurrent requests in my test cases.