1

I'm using Spring Boot 2.1. I'm also using the H2 in memory database. I have created this file at src/test/resources/data.sql:

insert into roles (id, name) values ('1be4965cb4f311eaa2b76a0000c30600', 'USER');
insert into roles (id, name) values ('1be6b2acb4f311eaa2b76a0000c30600', 'ADMIN');

insert into privileges (id, name) values ('1be4965cb4f311eaa2b76a0000c30600', 'USER');
insert into privileges (id, name) values ('1be6b2acb4f311eaa2b76a0000c30600', 'SUPER_ADMIN');

insert into roles_privileges (role_id, privilege_id) VALUES ('1be4965cb4f311eaa2b76a0000c30600', '1be4965cb4f311eaa2b76a0000c30600');
insert into roles_privileges (role_id, privilege_id) VALUES ('1be6b2acb4f311eaa2b76a0000c30600', '1be6b2acb4f311eaa2b76a0000c30600');

insert into occasions (id, name) values ('97c625b8b63511ea9d386a0000c30600', 'Birthday');

insert into users (id, email, enabled, first_name, last_name, password, token_expired) values ('aa7625b8b63512929d386a0000c30600', 'me@example.com', true, 'lone', 'ranger', 'password', false);

insert into users_roles (user_id, role_id) values ('aa7625b8b63512929d386a0000c30600', '1be6b2acb4f311eaa2b76a0000c30600');

I would like to create a spring boot integration test to test the following method ...

@RestController
@RequestMapping("/api/cards")
public class CardController {

    @Autowired
    private CardService cardService;
    
    @PostMapping
    @ResponseStatus(code = HttpStatus.CREATED)
    public void create(@RequestBody Card card, @AuthenticationPrincipal User loggedInUser) {
        card.setAuthor(loggedInUser);
        cardService.save(card);
    }

and I would like to load the one user in my database, but I'm not quite sure the easiest way to do that. I tried "@WithMockUser" but that isn't loading this user ...

@SpringBootTest(classes = CardmaniaApplication.class, 
    webEnvironment = WebEnvironment.RANDOM_PORT)
public class CardControllerIntegrationTest {

    @LocalServerPort
    private int port;
    
    @Autowired
    private TestRestTemplate restTemplate;
    
    @Autowired
    private ICardRepository cardRepository;

    @Autowired
    private IUserRepository userRepository;
    
    @Autowired
    private IOccasionRepository occasionRepository;
    
    @Autowired
    private JwtTokenUtil jwtTokenUtil;
    
    @Value("${jwt.http.request.header}")
    private String tokenHeader;
    
    @BeforeEach
    void setup() {
        UserDetails user = (UserDetails) userRepository.findAll().get(0);
        final String token = jwtTokenUtil.generateToken(user);
        restTemplate.getRestTemplate().setInterceptors(
                Collections.singletonList((request, body, execution) -> {
                    request.getHeaders()
                            .add(this.tokenHeader, "Bearer " + token);
                    return execution.execute(request, body);
                }));
    }
    
    @Test
    @WithMockUser(authorities = {"ADMIN"})
    void createCard() throws Exception {
        final Card card = new Card();
        final User author = userRepository.findAll().get(0);
        card.setAuthor(author);
        final Occasion occasion = occasionRepository.findAll().get(0);
        card.setOccasion(occasion);
        byte[] image = new byte[] {1, 1, 1, 1};
        card.setImage(image);
        
        ResponseEntity<String> responseEntity = this.restTemplate
                .postForEntity("http://localhost:" + port + "/api/cards", card, String.class);
            assertEquals(201, responseEntity.getStatusCodeValue());

        List<Card> cards = cardRepository.findByOccasion(occasion);
        assertThat(cards.size()).isEqualTo(1);
        final Card cardEntity = cards.get(0);
        assertThat(cardEntity.getImage()).isEqualTo(image);
    }
}

I'm fairly new to spring boot and so unfamiliar with the simplest way to pre-load a user into my security principal.

satish
  • 703
  • 5
  • 23
  • 52

2 Answers2

1

I discovered the answer is to use the "@WithUserDetails" annotation

@Test
@WithUserDetails("me@example.com")
void registrationWorksThroughAllLayers() throws Exception {

This will use your autowired class that implements UserDetailsService and invoke its loadUserByUsername with the annotated value, "me@example.com," in the code above.

satish
  • 703
  • 5
  • 23
  • 52
0

This setup is currently conflicting. One the one side you provide a mocked user for the SecurityContext using @WithMockUser and on the other side, you prepare a valid JWT token for actual authentication.

@WithMockUser is usually used in tests to e.g. easily access protected endpoints without creating a request with correct authentication information (like JWT or basic auth).

So you should pick one of the current approaches: Either go for mocking the user OR generate the JWT and access your endpoint.

The reason your @WithMockUser is not working currently, might be related to the default username Spring Security picks. If you want this to match your user in your database, consider configuring it: @WithMockUser(username="YOUR_USERNAME",roles={"USER","ADMIN"})

To debug your application further, you can temporarily add

Authentication authentication = SecurityContextHolder.getContext().getAuthentication();

to the endpoint you are testing and debug it to understand which user is now actually part of the SecurityContext.

rieckpil
  • 10,470
  • 3
  • 32
  • 56
  • Is there any way through annotations, whether through @WithMockUser or otherwise, to configure the logged in user built from a record in my H2 database? For example, it seems that "@WithMockUser" doesn't support the ID field, so I can't seem to load the logged in user ID from there. – satish Jun 30 '20 at 16:32
  • I guess not, but you can go without annotations and with the approach of using an actual JWT like you already do – rieckpil Jul 01 '20 at 05:37
  • Turns out the answer is to use '@WithUserDetails("me@example.com")'. I did not learn about this annotation until yesterday. – satish Jul 05 '20 at 18:07