Spring 5 с JUnit 5 + Mockito - метод контроллера возвращает значение null

Я пытаюсь протестировать метод с именем loadData, определенный в MainController, который возвращает результат в виде строки. Несмотря на то, что этот метод фактически возвращает данные, когда веб-приложение запускается в контейнере сервлета (или когда я отлаживаю код), данные не возвращаются, когда я вызываю его из тестового класса на основе JUnit 5 с Mockito.

Вот моя конфигурация:

@ContextConfiguration(classes = {WebAppInitializer.class, AppConfig.class, WebConfig.class})
@Transactional
@WebAppConfiguration
public class TestMainController {

    @InjectMocks
    private MainController mainController;

    private MockMvc mockMvc;

    @BeforeEach
    public void init() {
        mockMvc = MockMvcBuilders.standaloneSetup(this.mainController).build();
    }

    @Test
    public void testLoadData() throws Exception {
        MvcResult mvcResult = mockMvc
                .perform(MockMvcRequestBuilders.get("/loadData.ajax"))
                .andExpect(MockMvcResultMatchers.status().isOk()).andReturn();

        Assertions.assertNotNull(mvcResult.getResponse().getContentAsString(), "response should not be null");
    }

}

Тест не пройден из-за java.lang.NullPointerException, так как this.mainController равно null.

Сведения о среде:

Spring version: 5.0.3
JUnit version: 5.0.3
mockito version: 1.9.5
hamcrest version: 1.3
json-path-assert version: 2.2.0

Изменить: вот метод loadData для MainController:

@RequestMapping(value = "/loadData.ajax", method = RequestMethod.GET)
public String loadData(HttpServletRequest request, HttpServletResponse response) {
    List list = mainService.loadData(); // starts a transaction and invokes the loadData method of mainDAO repository which basically loads data from the database
    return JSONArray.fromObject(list).toString();
}

person talha06    schedule 11.02.2018    source источник
comment
Вы не вызываете метод контроллера напрямую с помощью MockHttp. Вы имитируете использование URL-адреса: docs.spring.io/spring-security/site/docs/current/reference/html/   -  person Guillaume F.    schedule 11.02.2018
comment
Допустим, я хочу протестировать URL-адрес loadData.ajax. Не могли бы вы помочь мне настроить тестовую среду? Пробовал следующее через руководство, которым вы поделились, но безуспешно: mockMvc.perform(get("/loadData.ajax"));   -  person talha06    schedule 11.02.2018
comment
Добавьте свой MainController. Что странно в вашем тесте, так это то, что у вас есть и веб-конфигурация, и обычная, разве у вас не должно быть ни одной?   -  person M. Deinum    schedule 11.02.2018
comment
@M.Deinum AppConfig предназначен для создания бинов session factory и transaction manager. WebConfig реализует WebMvcConfigurer и определяет все остальные необходимые bean-компоненты, включая view revolvers, interceptors, tasks и т. д.   -  person talha06    schedule 11.02.2018
comment
Вам не нужно Autowire контроллер. Вы создаете экземпляр MockMvc следующим образом. Если конструктор контроллера принимает какой-либо параметр, вы передаете его контроллеру. MockMvcBuilders.standaloneSetup(new YourController()).build();   -  person Ali.Wassouf    schedule 11.02.2018
comment
@Ali.Wassouf пытался, но не сработало public class TestMainController { @InjectMocks private MainController mainController; @Mock private MainService mainService; private MockMvc mockMvc; @BeforeEach public void setup() { // Process mock annotations MockitoAnnotations.initMocks(this); this.mockMvc = MockMvcBuilders.standaloneSetup(new MainController()).build(); } @Test public void testLoadData() throws Exception { this.mockMvc.perform(get("/loadData.ajax")).andExpect(status().isOk()); } }   -  person talha06    schedule 11.02.2018
comment
Как уже говорилось, добавьте свой MainController, чтобы мы могли видеть, что там происходит.   -  person M. Deinum    schedule 12.02.2018
comment
Невозможно объявить @SpringJUnitWebConfig и @SpringJUnitConfig в одном и том же тестовом классе. Spring будет учитывать только один из них, а именно первый найденный.   -  person Sam Brannen    schedule 12.02.2018
comment
@M.Deinum Спасибо за заботу; соответствующее определение метода добавлено в сообщение.   -  person talha06    schedule 13.02.2018
comment
Ваш тест неверен. Вы создаете имитируемый контроллер, но не имитируете зависимости и не определяете поведение. Вы также загружаете контекст, но фактически не используете его.   -  person M. Deinum    schedule 13.02.2018
comment
Итак, не могли бы вы предоставить правильный? @М.Дейнум   -  person talha06    schedule 13.02.2018


Ответы (1)


Вы можете вызвать метод контроллера напрямую, как мы это делаем для метода службы, но это не рекомендуется. Используя MockMvc, вы проверяете правильность заголовка и сопоставления параметров запроса. Кроме того, вы также проверяете правильность сопоставления конечных точек. Кроме того, МЕТОД запроса также является правильным. Все это вы не сможете проверить, если будете тестировать свой код, напрямую вызывая метод контроллера.

Одна вещь, которую вы можете попробовать, это вместо создания нового объекта внутри автономного контекста использовать Mock. то есть

mockMvc = MockMvcBuilders.standaloneSetup(this. mainController).build();

И во время звонка сделать это

MvcResult mvcResult = mockMvc
    .perform(MockMvcRequestBuilders.get("/loadData.ajax"))
    .andExpect(MockMvcResultMatchers.status().isOk()).andReturn();

Утверждайте, что вы хотели бы

Assert.assertEquals("response does not match", mvcResult.getResponse().getContentAsString(),
    "some expected response");

Вы получаете статус null или 400 или 404 http?

Если вы получаете 400, проверьте заголовок и запрос. param, если таковые имеются. Если вы получаете 404, проверьте URL-адрес. /loadData.ajax , предполагая, что это ваш путь сопоставления запросов в методе контроллера.

person lrathod    schedule 11.02.2018
comment
Получение NullPointerException в качестве this.mainController оказывается нулевым в строке при инициализации mockMvc. - person talha06; 13.02.2018
comment
Добавьте @RunWith(MockitoJUnitRunner.class) на уровне класса. Что-то вроде этого. @RunWith(MockitoJUnitRunner.class) открытый класс TestMainController {тогда ваша переменная mainController будет инициализирована - person lrathod; 13.02.2018
comment
Не удалось найти класс RunWith, который является частью класса JUnit 4. - person talha06; 13.02.2018
comment
импортировать org.junit.runner.RunWith; я вижу это в версии junit: 4.12. и в build.gradle testCompile(junit:junit:4.12) - person lrathod; 13.02.2018
comment
Оба JUnit 4 и 5 на пути к классам? Звучит не очень хорошо, так как аннотации определены для разных пакетов. Получение исключения No tests found in TestMainController Haven't you forgot @Test annotation? - person talha06; 13.02.2018
comment
Очень жаль. Забыл, что вы используете Junit5. Я еще не использовал JUnit5 с Mockito, но быстрый поиск привел меня к следующему: В JUnit 5 аннотация RunWith была заменена более мощной аннотацией ExtendWith. может быть, это поможет вам baeldung.com/junit-5-runwith - person lrathod; 13.02.2018
comment
нет проблем, но на самом деле MockitoJUnitRunner не совместим с аннотацией ExtendWith. - person talha06; 13.02.2018