Summary

When using JUnit 5 it seems that setting the setupBefore parameter of @WithUserDetails to TestExecutionEvent.TEST_EXECUTION does not work as intended. In fact it appears that the security context is created before the execution of a @BeforeEach method.

Actual Behavior

I wrote the following test class to showcase the issue

@ExtendWith(SpringExtension.class)
@ContextConfiguration
class WithUserDetailsTest {

  private static String USERNAME;

  @BeforeEach
  void setUp() {
    USERNAME = "dummy";
  }

  @Test
  @WithUserDetails(
      userDetailsServiceBeanName = "testUserDetailsService",
      setupBefore = TestExecutionEvent.TEST_EXECUTION
  )
  void test() {
    TestUserDetails principal = (TestUserDetails) SecurityContextHolder.getContext()
        .getAuthentication().getPrincipal();
    Assertions.assertNotNull(principal.getUsername());
  }

  @Configuration
  static class TestUserDetailsConfiguration {

    @Bean("testUserDetailsService")
    UserDetailsService testUserDetailsService() {
      return s -> new TestUserDetails(USERNAME);
    }
  }

  static class TestUserDetails implements UserDetails {

    private static final long serialVersionUID = -6779736987183888865L;

    private String username;

    TestUserDetails(String username) {
      this.username = username;
    }

    @Override
    public String getUsername() {
      return username;
    }

    ...
  }
}

What happens here is that the test keeps failing because when the UserDetails is actually loaded the USERNAME variable is still null.

Expected Behavior

I would expect the @BeforeEach method to be executed before the SecurityContext initialization therefore allowing the creation of a UserDetails with a non null username.

Configuration

Version

I am using Spring Boot Dependency 2.1.3.RELEASE (=> Spring Security Test 5.1.4.RELEASE).

Sample

Comment From: jfriedenstab

I stumbled upon this issue, too. I would like to create a custom UserDetails object in the @BeforeEach method which can then be used by @WithUserDetails. Are there any plans to provide a fix for this?

Comment From: piotr-karon

For anyone with this problem, as this is still not resolved. Instead of saving user to repository in @Before or @BeforeEach create separate method annotated with @PostConstruct and save user. Assuming you have correctly implemented custom UserDetailsService and @SpringBootTest picks it (or you provide bean name in @WithUserDetails)

private const val USERNAME = "username"
private val user = User(USERNAME)

@SpringBootTest
@WithUserDetails(USERNAME)
class TestClass @Autowired constructor(
  private val userRepo: UserRepository
){

  @PostConstruct
  fun saveUser(){
    userRepo.save(user)
  }

  @Test
  fun someTest(){
     <user is available here>
  }
}

Comment From: sambernet

Not specific to JUnit 5, same problem also affects vintage JUnit4 tests. But looking at the brand new PR this was a general issue with the WithUserDetails and both should be fixed with this change. 🥳 Thanks 👍

Comment From: rwinch

This is now fixed via gh-6591 Closing as duplicate of the PR