MockBean не инициализирует служебный компонент при использовании Mock Mvc standaloneSetup - PullRequest
0 голосов
/ 15 апреля 2020

Здесь я тестирую свою конечную точку, используя WebMvcTest, MockMvc, и службу имитации, используя @MockBean. Без использования метода standaloneSetup приведенный ниже код работает нормально.

public class MessageControllerTest {

    @Nested
    @WebMvcTest
    class TestUsingMockServer {

        @MockBean
        MessageServiceImpl messageService;

        @Autowired
        MockMvc mockMvc;

        @Test
        public void test_to_return_id_with_message_json() throws Exception {

            when(messageService.findById(anyLong())).thenAnswer(invocation -> new Message("Hello World", (Long) invocation.getArguments()[0], LocalDateTime.now()));

            mockMvc.perform(get("/resources/messages/{id}", 3)
                                    .contextPath("/resources")
                                    .accept(MediaType.APPLICATION_JSON))
                    .andDo(print())
                    .andExpect(content().contentType(MediaType.APPLICATION_JSON))
                    .andExpect(status().isOk())
                    .andExpect(result -> {
                        result.toString().contains("3");
                        result.toString().contains("Hello World");
                    });

        }


        @Test
        public void test_to_get_the_name_passed() throws Exception {

            when(messageService.getMessageByIdName(anyLong(), anyString())).thenAnswer(invocation -> new Message("Hello " + invocation.getArguments()[1],
                    (Long) invocation.getArguments()[0], LocalDateTime.now()));


            mockMvc.perform(get("/resources/messages/{id}", 3)
                                    .queryParam("name", "kaustubh")
                                    .contextPath("/resources")
                                    .accept(MediaType.APPLICATION_JSON))
                    .andDo(print())
                    .andExpect(content().contentType(MediaType.APPLICATION_JSON))
                    .andExpect(status().isOk())
                    .andExpect(result -> {
                        result.toString().contains("3");
                        result.toString().contains("kaustubh");
                    });

        }

    }
}

Чтобы избежать повторения при добавлении метода standaloneSetup и выполнении тестов, я получаю сообщение об ошибке, в котором говорится, что компонент MessageServiceImpl не инициализирован (поскольку из NullPointerException)

    public class MessageControllerTest {

        @Nested
        @WebMvcTest
        class TestUsingMockServer {

            @MockBean
            MessageServiceImpl messageService;

            @Autowired
            MockMvc mockMvc;

            @BeforeEach
            public void setUp(){
                mockMvc = standaloneSetup(new MessageController())
                                  .defaultRequest(get("/")
                                                          .contextPath("/resources")
                                                          .accept(MediaType.APPLICATION_JSON)
                                  ).build();
            }

            @Test
            public void test_to_return_id_with_message_json() throws Exception {

                when(messageService.findById(anyLong())).thenAnswer(invocation -> new Message("Hello World", (Long) invocation.getArguments()[0], LocalDateTime.now()));

                mockMvc.perform(get("/resources/messages/{id}",3))
                        .andDo(print())
                        .andExpect(content().contentType(MediaType.APPLICATION_JSON))
                        .andExpect(status().isOk())
                        .andExpect(result -> {
                            result.toString().contains("3");
                            result.toString().contains("Hello World");
                        });

            }
}
}

Выдает следующую ошибку

Error showing NullPointerException in MessageController

Строка 17, как указано, в ошибке вызывает MessageServiceImpl

@RestController
@RequestMapping("/messages")
public class MessageController {

    @Autowired
    MessageServiceImpl messageService;

    @GetMapping(path = "/{id}")
    public Message getMessageById(@PathVariable Long id) {
        return messageService.findById(id);  // LINE 17
    }

    @GetMapping(path = "/{id}", params = "name")
    public Message getMessageByIdName(@PathVariable Long id, @RequestParam(value = "name", defaultValue = "ST") String name) {
        return messageService.getMessageByIdName(id, name);
    }

}

Происходит потому, что построитель MockMvc настроен до создания компонента службы?

1 Ответ

0 голосов
/ 15 апреля 2020

Есть несколько вещей, которые вы должны знать о настройке ваших тестов.

В вашем первом примере:

@WebMvcTest
class TestUsingMockServer {

    @MockBean
    MessageServiceImpl messageService;

    @Autowired
    MockMvc mockMvc;

Здесь контекст теста запускается @WebMvcTest и поэтому Mock Mvc может быть подключен автоматически. @WebMvcTest также посмотрит на @Mockbean, чтобы увидеть, что нужно смоделировать.

Во втором нашем примере:

    @WebMvcTest
    class TestUsingMockServer {

        @MockBean
        MessageServiceImpl messageService;

        @Autowired
        MockMvc mockMvc;

        @BeforeEach
        public void setUp(){
            mockMvc = standaloneSetup(new MessageController())
                              .defaultRequest(get("/")
                                                      .contextPath("/resources")
                                                      .accept(MediaType.APPLICATION_JSON)
                              ).build();
        }

Здесь вы переопределяете свой объект @Autowired mock Mvc другим пример. И здесь вы настроите контекст,

        mockMvc = standaloneSetup(new MessageController())

Эта настройка пропустит любую автопроводку, поэтому в вашем классе MessageController,

@Autowired
MessageServiceImpl messageService;

Это будет пропущено. и будет нулевым.

Так что для решения. Простым решением будет сделать автоматическое подключение через конструктор:

@Autowired
public MessageController(MessageServiceImpl messageService) {
 this.messageService = messageService;
}

Теперь вы можете просто добавить свой макет в свой тестовый класс:

mockMvc = standaloneSetup(new MessageController(messageService))

Это решит вашу проблему.

Еще один совет, который я хочу дать, это то, что вам не нужно сильно беспокоиться о повторении ваших тестов. Они будут работать очень быстро после загрузки контекста.

...