javaspringjunitspring-test

Using different Auth mock up in Spring Junit tests


I'm using Spring and Junit4 to tests my server application. My application uses Spring Security and some queries take advantage of SpEL syntax to check the logged user.

Because I'm testing some scenarios that needs some data loaded I've some issues because I've not right to do that.

For example:

@Test
    @WithUserDetails(userDetailsServiceBeanName = "mockUserDetailService", value = "customer")
    public void saveEcommerceTicket() {
        ZonedDateTime entryDate = ZonedDateTime.now().plusDays(1);
        createStandardFare();
        createDailyCode(entryDate.toInstant());
        createPaymentWithWallet();
        Customer customer = createCustomerWithWallet(new BigDecimal(100));

        Ticket ticket = new Ticket();
        ticket.setLicensePlate1("AA123BB");
        ticket.setArea(areaCentroStorico);
        ticket.setDailyCode("CC");
        ticket.setEntryDate(entryDate.toInstant());
        ticket.setExitDate(entryDate.plusDays(1).toInstant());
        ticket.setPassengers(50);
        ticket.setPassengersCountry(italy);
        ticket.setCustomer(customer);

        ticket = ticketService.saveFromEcommerceWithWalletPayment(ticket, new ArrayList<Media>());

        assertEquals(true, ticket.isStandard());
        assertEquals(TicketStatus.VALID, ticket.getStatus());
        assertTrue(new BigDecimal(100).compareTo(ticketRepository.sumPayments(ticket.getId())) == 0);
        assertTrue(BigDecimal.ZERO.compareTo(customer.getWallet().getBalance()) == 0);
        assertEquals(true, ticket.isPaid());
    }

This is an example of test where I use a custom userdetailservice. Unfortunately some actions like: createStandardFare(), createDailyCode(), etc, requires a different role. I tried to set up manually authentication but I end up always to have the entire test with one ROLE. Instead I need that some parts are executed with a specific ROLE (let's say ADMIN) and some others with another (let's say CUSTOMER).

Is there a way to accomplish this with Spring and Junit?


Solution

  • If you are invoking different methods within a single test method that require different roles, you only really have two options.

    1. Configure a mock user (e.g., via @WithMockUser or similar) that is assigned all of the roles used within the test method.
    2. Switch the current mock user programmatically within the test method itself.
      • For example, using the following code snippets.
    @Test
    public void test() {
        login("customer", "password", "ROLE_CUSTOMER");
        // ...
        login("admin", "password", "ROLE_ADMIN");
        // ...
    }
    
    static void login(String username, String password, String... roles) {
        SecurityContext context = SecurityContextHolder.createEmptyContext();
    
        User user = new User(username, password, roles(roles));
    
        Authentication auth = new UsernamePasswordAuthenticationToken(user, password, user.getAuthorities());
        context.setAuthentication(auth);
        SecurityContextHolder.setContext(context);
    }
    
    static List<SimpleGrantedAuthority> roles(String... roles) {
        return Arrays.stream(roles).map(SimpleGrantedAuthority::new).collect(toList());
    }
    

    Note: User in the above example is org.springframework.security.core.userdetails.User.