Home > Software engineering >  Spring Boot Microservice JUnit Test - How can I get jwt token from one service and use it another se
Spring Boot Microservice JUnit Test - How can I get jwt token from one service and use it another se

Time:11-16

I have a problem about getting jwt token from one service and use it in the test method of one service.

I tried to write JUnit Controller test in payment, product and order service in my spring boot microservice example. After I defined auth service to handle with creating user and login with jwt token and defined api gateway to use JWTFilter, it is required to define bearer token for each request of each test request method.

Normally, it works but I have to get jwt token and use it but I have no idea how to get it?

Here is an one test method of PaymentControllerTest shown below. As you can see, there is no defination of Authorization Bearer. How can I define it?

@Test
    @DisplayName("Place Order -- Success Scenario")
    void test_When_placeOrder_DoPayment_Success() throws Exception {

        OrderRequest orderRequest = getMockOrderRequest();
        String jwt = getJWTTokenForRoleUser();

        MvcResult mvcResult
                = mockMvc.perform(MockMvcRequestBuilders.post("/order/placeorder")
                        .contentType(MediaType.APPLICATION_JSON_VALUE)
                        .header("Authorization", "Bearer "   jwt)
                        .content(objectMapper.writeValueAsString(orderRequest)))
                .andExpect(MockMvcResultMatchers.status().isOk())
                .andReturn();

        String orderId = mvcResult.getResponse().getContentAsString();

        Optional<Order> order = orderRepository.findById(Long.valueOf(orderId));
        assertTrue(order.isPresent());

        Order o = order.get();
        assertEquals(Long.parseLong(orderId), o.getId());
        assertEquals("PLACED", o.getOrderStatus());
        assertEquals(orderRequest.getTotalAmount(), o.getAmount());
        assertEquals(orderRequest.getQuantity(), o.getQuantity());
    }

Here are methods regarding the jwt token process

private String getJWTTokenForRoleUser(){

    var loginRequest = new LoginRequest("User1","user1");

    String jwt = jwtUtils.generateJwtToken(loginRequest.getUsername());

    return jwt;
    }

@Data
@AllArgsConstructor
@NoArgsConstructor
public class LoginRequest {

  private String username;
  private String password;

}

Here is the jwtutil class shown below

@Component
public class JwtUtils {

    private static final Logger LOGGER = LoggerFactory.getLogger(JwtUtils.class);

    @Value("${jwt.secret}")
    private String jwtSecret;

    @Value("${jwt.expireMs}")
    private int jwtExpirationMs;

    public String generateJwtToken(String username) {
        return generateTokenFromUsername(username);
    }

    public String generateTokenFromUsername(String username) {
        return Jwts.builder().setSubject(username).setIssuedAt(new Date())
                .setExpiration(new Date((new Date()).getTime()   jwtExpirationMs))
                .signWith(SignatureAlgorithm.HS512, jwtSecret).compact();
    }
}

To run the app, 1 ) Run Service Registery (Eureka Server)

2 ) Run config server

3 ) Run zipkin and redis through these commands shown below on docker

docker run -d -p 9411:9411 openzipkin/zipkin 
docker run -d --name redis -p 6379:6379 redis

4 ) Run api gateway

5 ) Run auth service,product service,order service and payment service

Edited (I defined all properites in application-test.yaml but they couldn't be fetched from OrderControllerTest)

Caused by: java.lang.IllegalArgumentException: Could not resolve placeholder 'jwt.secret' in value "${jwt.secret}"

Here is the repo : Link

CodePudding user response:

you can create a UTIL and get a token from there and use in your test cases

CodePudding user response:

You can't write inter services tests with mocked JWTs: JWTs are signed and you won't have access to authorization server private key.

Either write

  • tests in mocked HTTP environment : @webMvcTest or @SpringBootTest(webEnvironment = WebEnvironment.MOCK) in which case calls to other services should be mocked (with wire mock or something) and so will be Authentication in test security context (no JWT at all in that case)
  • "full" integration tests with all services up, a "real" client (WebClient, RestTemplate, @FeignClient, or actual UI manipulated with Protractor or something) and real tokens (that is issued by real authorization server issued for real users)

The second solution is clearly more complicated and longer to learn, setup and maintain. Also, tests will be slower and fragile. Extensive "full" integration testing is a hell, frequently considered as a worst practice.

Your project is not even close to run integration tests (no parent pom to manage inter modules dependencies and integration tests configuration, miss configured modules,...)

Start with unit tests: @WebMvcTest for a single @Controller With @MockBean for used @Components like @Services and @Repository

Then write integration tests scoped to one micro-service: @SpringBoot tests with wiremock to simulate exchanges with other micro-services.

Only then, maybe, will you write full integration tests involving several micro-services, an authorization-server and a "rich" or a REST client (with a utility method to go through authorization-code flow if you want to simulate a user).

For now, I strongly advise you focus on the first two kind of tests, following those tutorials. The samples of the same repo also demo how to write unit tests for @Services and @Repository with security expressions.

  • Related