spring-securityintegration-testingspring-boot-testspring-boot-2

How do I pre-load my Spring Boot integration test with a user from my H2 database?


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.


Solution

  • 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.