4

Currently struggling with problem when I get 'mapping error for request' with following controller/test configuration. Controller:

@Slf4j
@Validated
@RestController
@RequiredArgsConstructor
public class AdtechController {

private final AdtechService adtechService;

@PostMapping(value = "/subscriber/session")
public ResponseEntity<ResponseDto> submitSession(@RequestBody RequestDto requestDto) {
    log.trace("execute submitSession with {}", requestDto);
    ResponseDtoresponse = adtechService.submitSession(requestDto);
    return new ResponseEntity<>(response, HttpStatus.OK);
}

@ExceptionHandler(AdtechServiceException.class)
public ResponseEntity<AdtechErrorResponse> handleAdtechServiceException(AdtechServiceException e) {
    return new ResponseEntity<>(new AdtechErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR.value(), e.getMessage()), HttpStatus.INTERNAL_SERVER_ERROR);
}

}

Test:

@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
@SpringJUnitConfig({AdtechTestConfig.class})
public class AdtechControllerTest {

private static final ObjectMapper OBJECT_MAPPER = JsonUtil.getJackson();

@Autowired
private MockMvc mockMvc;

@Test
public void testSubmitSession() throws Exception {
    RequestDto requestDto = new RequestDto ();
    requestDto.setKyivstarId("1123134");
    requestDto.setMsisdn("123476345242");
    requestDto.setPartnerId("112432523");
    requestDto.setPartnerName("125798756");
    String request = OBJECT_MAPPER.writeValueAsString(requestDto);
    System.out.println("REQUEST: " + request);
    String response = OBJECT_MAPPER.writeValueAsString(new ResponseDto("123"));
    System.out.println("RESPONSE: " + response);
    mockMvc.perform(post("/subscriber/session")
            .content(MediaType.APPLICATION_JSON_VALUE)
            .content(request))
            .andDo(print())
            .andExpect(status().isOk())
            .andExpect(content().string(containsString(response)));

}

}

Configuration:

@Configuration
public class AdtechTestConfig {

@Bean
public AdtechService adtechTestService() {
    return requestDto -> new AdtechResponseDto("123");
}

}

After test execution I get No mapping for POST /subscriber/session

The reason for the struggle is that my code from other modules with the same configuration works fine. Can somebody point out what am I missing ? Thanks in advance!

Miklosh
  • 139
  • 2
  • 10
  • For starters make yp your mind on what you want to use, Junit4 or Junit5. Currently you appear to be mixing both. – M. Deinum Aug 04 '20 at 17:00
  • @M.Deinum you mean this part : @RunWith(SpringRunner.class) ? I've removed it and now it looks like this: SpringBootTest AutoConfigureMockMvc SpringJUnitConfig({AdtechTestConfig.class}) still fails with same error – Miklosh Aug 04 '20 at 17:36
  • Which `@Test` annotation are you using? ALso remove the `@SpringJUnitConfig` you want to use `@SpringBootTest` then don't use this annotation. – M. Deinum Aug 04 '20 at 17:37
  • @M.Deinum I'm using org.junit.jupiter.api.Test – Miklosh Aug 04 '20 at 17:45
  • So you are now using JUnit5. The config overrides the spring boot test and I doubt your test config (why do you need it?) is only a half version of what actually needs to be loaded. So remove it, you should have the `@SpringBootTest` and that should be enough. – M. Deinum Aug 04 '20 at 17:48
  • @M.Deinum I can't remove SpringJUnitConfig({AdtechTestConfig.class}), I'm using separate configuration for test classes in test package, it's not recognized by SpringBootTest by default – Miklosh Aug 04 '20 at 17:49
  • @M.Deinum I need this config to mock the service I'm using in controller – Miklosh Aug 04 '20 at 17:50
  • No you don't. Use `@MockBean` for that in your test class. Basically your app contains only your service and nothing more, your controllers aren't loaded. If you only want to test your controller I would suggest using `@WebMvcTest` instead of `@SpringBootTest` to load only the web part and use `@MockBean` for your service. – M. Deinum Aug 04 '20 at 17:50

3 Answers3

3

Apparently you are loading a configuration class to mock beans, this interferes with the other parts of Spring Boot and probably leads to partially loading your application. I suspect only the mocked service is available.

Instead of the test configuration use @MockBean to create a mock for the service and register behaviour on it.

@SpringBootTest
@AutoConfigureMockMvc
public class AdtechControllerTest {

  private static final ObjectMapper OBJECT_MAPPER = JsonUtil.getJackson();

  @Autowired
  private MockMvc mockMvc;
  @MockBean
  private AdtechService mockService;

  @BeforeEach
  public void setUp() {
    when(mockService.yourMethod(any()).thenReturn(new AdtechResponseDto("123")); 
  }


  @Test
  public void testSubmitSession() throws Exception {
    // Your original test method
  }
}

If the only thing you want to test is your controller you might also want to consider using @WebMvcTest instead of @SpringBootTest.

@WebMvcTest(AdTechController.class)
public class AdtechControllerTest {

  private static final ObjectMapper OBJECT_MAPPER = JsonUtil.getJackson();

  @Autowired
  private MockMvc mockMvc;
  @MockBean
  private AdtechService mockService;

  @BeforeEach
  public void setUp() {
    when(mockService.yourMethod(any()).thenReturn(new AdtechResponseDto("123")); 
  }


  @Test
  public void testSubmitSession() throws Exception {
    // Your original test method
  }
}

This will load a scaled-down version of the context (only the web parts) and will be quicker to run.

M. Deinum
  • 115,695
  • 22
  • 220
  • 224
  • thank you very much, your advice to use WebMvcTest(AdTechController.class) really did the trick! – Miklosh Aug 04 '20 at 18:18
1

try this:

@Slf4j
@Validated
@RestController
@RequiredArgsConstructor
public class AdtechController {

private AdtechService adtechService;

public AdtechController (AdtechService adtechService) {
        this.adtechService= adtechService;
}

@PostMapping(value = "/subscriber/session")
public ResponseEntity<ResponseDto> submitSession(@RequestBody RequestDto requestDto) {
    log.trace("execute submitSession with {}", requestDto);
    ResponseDtoresponse = adtechService.submitSession(requestDto);
    return new ResponseEntity<>(response, HttpStatus.OK);
}

@ExceptionHandler(AdtechServiceException.class)
public ResponseEntity<AdtechErrorResponse> handleAdtechServiceException(AdtechServiceException e) {
    return new ResponseEntity<>(new AdtechErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR.value(), e.getMessage()), HttpStatus.INTERNAL_SERVER_ERROR);
}

}

Test:

@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
@SpringJUnitConfig({AdtechTestConfig.class})
public class AdtechControllerTest {

private static final ObjectMapper OBJECT_MAPPER = JsonUtil.getJackson();

@Autowired
private MockMvc mockMvc;
@Autowired
private AdtechService adtechService;

@Before
    public void setUp() {
        MockitoAnnotations.initMocks(this);
        this.mvc = MockMvcBuilders.standaloneSetup(new AdtechController(adtechService)).build();
}

@Test
public void testSubmitSession() throws Exception {
    RequestDto requestDto = new RequestDto ();
    requestDto.setKyivstarId("1123134");
    requestDto.setMsisdn("123476345242");
    requestDto.setPartnerId("112432523");
    requestDto.setPartnerName("125798756");
    String request = OBJECT_MAPPER.writeValueAsString(requestDto);
    System.out.println("REQUEST: " + request);
    String response = OBJECT_MAPPER.writeValueAsString(new ResponseDto("123"));
    System.out.println("RESPONSE: " + response);
    mockMvc.perform(post("/subscriber/session")
            .content(MediaType.APPLICATION_JSON_VALUE)
            .content(request))
            .andDo(print())
            .andExpect(status().isOk())
            .andExpect(content().string(containsString(response)));

}

}
Mauro Valc
  • 23
  • 1
  • 5
1

Is the AdtechTestConfig.class introducing the /ad-tech path segment in to your test request? If so, this is why your test is trying the path /ad-tech/subscriber/session instead of /subscriber/session.

If this is actually the correct uri, then you may add @RequestMapping to the controller like below or just to the post method itself

@Slf4j
@Validated
@RestController
@RequestMapping("/ad-tech")
@RequiredArgsConstructor
public class AdtechController {

private final AdtechService adtechService;

@PostMapping(value = "/subscriber/session")
public ResponseEntity<ResponseDto> submitSession(@RequestBody RequestDto requestDto) {
...
Joseph Berry
  • 168
  • 1
  • 10