Проблема с тестированием Spring MVC среза в SpringBoot 1.4

Я опробую новые возможности тестирования Spring Boot 1.4 MVC. У меня есть следующий контроллер.

@Controller
public class ProductController {

  private ProductService productService;

  @Autowired
  public void setProductService(ProductService productService) {
    this.productService = productService;
  }

  @RequestMapping(value = "/products", method = RequestMethod.GET)
  public String list(Model model){
    model.addAttribute("products", productService.listAllProducts());
     return "products";
  }
}

Моя минимальная реализация ProductService:

@Service
public class ProductServiceImpl implements ProductService {
  private ProductRepository productRepository;

  @Autowired
  public void setProductRepository(ProductRepository productRepository) {
    this.productRepository = productRepository;
  }

  @Override
  public Iterable<Product> listAllProducts() {
    return productRepository.findAll();
  }

}

Код ProductRepository:

public interface ProductRepository extends CrudRepository<Product,    
 Integer>{
}

Я пытаюсь использовать новый @WebMvcTest для тестирования контроллера. Мой взгляд - это тимлеф. И мой тест контроллера это:

@RunWith(SpringRunner.class)
@WebMvcTest(ProductController.class)

public class ProductControllerTest {
private MockMvc mockMvc;

@Before
public void setUp() {
    ProductController productController= new ProductController();       
    mockMvc = MockMvcBuilders.standaloneSetup(productController).build();
}

@Test
public void testList() throws Exception {        
mockMvc.perform(MockMvcRequestBuilders.get("/products"))                 
.andExpect(MockMvcResultMatchers.status().isOk())                
.andExpect(MockMvcResultMatchers.view().name("products"))             
 .andExpect(MockMvcResultMatchers.model().attributeExists("products"));               
 }
}

Но при запуске теста я получаю эту ошибку.

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'productController': Unsatisfied dependency expressed through method 'setProductService' parameter 0: No qualifying bean of type [guru.springframework.services.ProductService] found for dependency [guru.springframework.services.ProductService]: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {}; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [guru.springframework.services.ProductService] found for dependency [guru.springframework.services.ProductService]: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {}

Мне нужна помощь, чтобы решить проблему, чтобы правильно проверить ProductController. Будем весьма благодарны за дополнительные предложения и Expect() для более тщательного тестирования контроллера.

Заранее спасибо.

4 ответа

Решение

Ты используешь @WebMvcTest в то же время вручную настраивая MockMvc пример. Это не имеет смысла в качестве одной из основных целей @WebMvcTest это автоматически настроить MockMvc экземпляр для вас. Кроме того, в вашей ручной конфигурации вы используете standaloneSetup Это означает, что вам нужно полностью настроить тестируемый контроллер, включая введение в него любых зависимостей. Вы не делаете то, что вызывает NullPointerException,

Если вы хотите использовать @WebMvcTest, и я бы порекомендовал вам сделать, вы можете удалить свой setUp метод полностью и имеет автоматически настроенный MockMvc Экземпляр впрыскивается вместо использования @Autowired поле.

Затем, чтобы контролировать ProductService это используется ProductControllerВы можете использовать новый @MockBean аннотация для создания макета ProductService который затем будет введен в ProductController,

Эти изменения оставляют ваш тестовый класс похожим на это:

package guru.springframework.controllers;

import guru.springframework.services.ProductService;
import org.hamcrest.Matchers;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;

import static org.assertj.core.api.Assertions.assertThat;

@RunWith(SpringRunner.class)
@WebMvcTest(ProductController.class)
public class ProductControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @MockBean
    private ProductService productService;

    @Test
    public void testList() throws Exception {
      mockMvc.perform(MockMvcRequestBuilders.get("/products"))
                .andExpect(MockMvcResultMatchers.status().isOk())
                 .andExpect(MockMvcResultMatchers.view().name("products"))
                 .andExpect(MockMvcResultMatchers.model().attributeExists("products"))
               .andExpect(MockMvcResultMatchers.model().attribute("products",
                        Matchers.is(Matchers.empty())));

    }
}

Кто заинтересован в загрузке полной версии приложения, попробуйте использовать @SpringBootTest в сочетании с @AutoConfigureMockMvc а не @WebMvcTest,

Я долго боролся с этой проблемой, но в итоге я получил полную картину.
Многие учебные пособия в Интернете, а также официальная документация Spring, которую я нашел до сих пор, утверждают, что вы можете тестировать свои контроллеры, используя @WebMvcTest; это совершенно правильно, но все же пропускаю половину истории.
Как указано в Javadoc такой аннотации, @WebMvcTest предназначен только для тестирования ваших контроллеров, и не будет загружать все компоненты вашего приложения вообще, и это сделано специально.
Это даже несовместимо с явными аннотациями сканирования компонентов, такими как @Componentscan,

Я предлагаю всем, кто интересуется этим вопросом, прочитать полный Javadoc аннотации (длиной всего 30 строк и заполненный сжатой полезной информацией), но я извлеку пару драгоценных камней, относящихся к моей ситуации.

из типа аннотации WebMvcTest

Использование этой аннотации отключит полную автоконфигурацию и вместо этого будет применять только конфигурацию, относящуюся к тестам MVC (т.е. @Controller, @ControllerAdvice, @JsonComponent Фильтр, WebMvcConfigurer а также HandlerMethodArgumentResolver бобы, но не @Component, @Service или же @Repository фасоль). [...] Если вы хотите загрузить полную конфигурацию приложения и использовать MockMVC, вам следует подумать @SpringBootTest в сочетании с @AutoConfigureMockMvc а не эта аннотация.

И на самом деле, только @SpringBootTest + @AutoConfigureMockMvc исправил мою проблему, все другие подходы, которые использовали @WebMvcTest не удалось загрузить некоторые необходимые компоненты.

РЕДАКТИРОВАТЬ

Я забрал свой комментарий о документации Spring, потому что не знал, что подразумевается срез при использовании @WebMvcTest; на самом деле в документации по слайсам MVC ясно, что загружается не все приложение, что по самой природе слайса.

Пользовательский тестовый срез с Spring Boot 1.4

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

Вместо автоматической разводки MockMvc я создал экземпляр объекта mockmvc на этапе настройки следующим образом.

protected void setUp() {
        mvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
    }

На всякий случай, если вы добавили следующие декораторы, но они все еще не работают:

      import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;

@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
public class SomeTest {

    @Autowired
    private MockMvc mockMvc;


    @Test
    public void somePositiveTest() throws Exception {
        mockMvc.perform(MockMvcRequestBuilders.get(url))
                .andExpect(status().is2xxSuccessful());
    }
}

убедитесь, что вы добавили следующую зависимость в свой pom.xml:

      <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
Другие вопросы по тегам