Home > Back-end >  Spring Boot JUnit tests fail with Status expected:<200> but was:<404>
Spring Boot JUnit tests fail with Status expected:<200> but was:<404>

Time:11-02

For some time I've been struggling to make JUnit tests for my rest controller. For some reason, every time I try to run them I get the error Status expected:<200> but was:<404>. Here is my controller:

@RestController
@RequestMapping("/travels")
@RequiredArgsConstructor
public class TravelController {

  private final TravelService travelService;

  private final TravelOutputDtoMapper travelOutputDtoMapper;

  @GetMapping
  public List<TravelOutputDto> getAll() {
    List<Travel> travels = travelService.getAll();

    return travels.stream()
            .map(travelOutputDtoMapper::travelToTravelOutputDto)
            .collect(Collectors.toList());
  }
}

And here is my test:

@ExtendWith(SpringExtension.class)
@WebMvcTest(controllers = TravelController.class)
@ContextConfiguration(classes = {
    TravelOutputDtoMapper.class,
    TravelOutputDtoMapperImpl.class
})
class TravelControllerTest {

  @Autowired
  private MockMvc mockMvc;

  @MockBean
  private TravelService travelService;
  @Autowired
  private TravelOutputDtoMapper travelOutputDtoMapper;

  @Test
  void testGetAll() throws Exception {

      List<Travel> travels = mockTravelList();

      Mockito.when(travelService.getAll()).thenReturn(travels);


      mockMvc.perform(get("/travels"))
              .andExpect(status().isOk());
  }


  private List<Travel> mockTravelList() {
  // Dummy travel list
  }
}

I think the reason is connected with TravelOutputDtoMapper as if I remove it from the controller and don't try to inject it the tests are passing, but I cannot find any information why it is doing it. The autowired mapper has an instance and works just fine.

Here is the Mapper:

@Mapper(componentModel = "spring")
public interface TravelOutputDtoMapper {

  @Mapping(target = "from", source = "entity.from.code")
  @Mapping(target = "to", source = "entity.to.code")
  TravelOutputDto travelToTravelOutputDto(Travel entity);
}

CodePudding user response:

The @ContextConfiguration annotation is used for a different purpose:

@ContextConfiguration defines class-level metadata that is used to determine how to load and configure an ApplicationContext for integration tests.

Using Spring Boot and @WebMvcTest there's no need to manually specify how to load the context. That's done for you in the background.

If you'd use this annotation, you'd specify your main Spring Boot class here (your entry-point class with the @SpringBootApplication annotation).

From what I can see in your test and your question is that you want to provide an actual bean for the TravelOutputDtoMapper, but mock the TravelService.

In this case, you can use @TestConfiguration to add further beans to your sliced Spring TestContext:

// @ExtendWith(SpringExtension.class) can be removed. This extension is already registered with @WebMvcTest
@WebMvcTest(controllers = TravelController.class)
class TravelControllerTest {

  @Autowired
  private MockMvc mockMvc;

  @MockBean
  private TravelService travelService;

  @Autowired
  private TravelOutputDtoMapper travelOutputDtoMapper;


  @TestConfiguration
  static class TestConfig {
 
    @Bean
    public TravelOutputDtoMapper travelOutputDtoMapper() {
      return new TravelOutputDtoMapper(); // I assume your mapper has no collaborators 
    }
  }

  // ... your MockMvc tests
}
  • Related