J'essaie les nouvelles fonctionnalités de test Spring Boot 1.4 MVC. J'ai le contrôleur suivant.
@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";
}
}
Mon implémentation minimale de ProductService est:
@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();
}
}
Le code de ProductRepository est:
public interface ProductRepository extends CrudRepository<Product,
Integer>{
}
J'essaie d'utiliser le nouveau @WebMvcTest pour tester le contrôleur. Mon point de vue est une plaque d'équipe de thymeleaf. Et mon test de contrôleur est le suivant:
@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"));
}
}
Mais, en exécutant le test, j'obtiens cette erreur.
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: {}
J'ai besoin d'aide pour résoudre le problème afin de tester correctement ProductController. Des suggestions supplémentaires etExpect () pour des tests plus approfondis du contrôleur seront très appréciées.
Merci d'avance.
Vous utilisez @WebMvcTest
tout en configurant manuellement une instance MockMvc
. Cela n'a pas de sens comme l'un des principaux objectifs de @WebMvcTest
consiste à configurer automatiquement une instance MockMvc
pour vous. De plus, dans votre configuration manuelle, vous utilisez standaloneSetup
, ce qui signifie que vous devez configurer entièrement le contrôleur en cours de test, y compris y injecter toutes les dépendances. Vous ne faites pas ce qui provoque le NullPointerException
.
Si vous souhaitez utiliser @WebMvcTest
, et je vous recommande de le faire, vous pouvez supprimer entièrement votre méthode setUp
et avoir une instance MockMvc
auto-configurée injectée à la place à l'aide d'un @Autowired
champ.
Ensuite, pour contrôler le ProductService
utilisé par ProductController
, vous pouvez utiliser le nouveau @MockBean
annotation pour créer une maquette ProductService
qui sera ensuite injectée dans ProductController
.
Ces modifications laissent votre classe de test ressembler à ceci:
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())));
}
}
Les personnes intéressées par le chargement de l'application complète devraient essayer d'utiliser @SpringBootTest
combiné avec @AutoConfigureMockMvc
plûtot que le @WebMvcTest
.
Je me bats avec le problème depuis un bon moment, mais j'ai finalement obtenu l'image complète.
Les nombreux tutoriels sur Internet, ainsi que la documentation officielle du printemps que j'ai trouvée jusqu'à présent , indiquez que vous pouvez tester vos contrôleurs à l'aide de @WebMvcTest
; c'est tout à fait correct, en omettant toujours la moitié de l'histoire.
Comme le souligne le javadoc d'une telle annotation, @WebMvcTest
est uniquement destiné à tester vos contrôleurs et ne chargera pas du tout tous les beans de votre application , et cela est voulu par la conception même du produit.
Il est même incompatible avec les annotations d'analyse de bean explicites comme @Componentscan
.
Je suggère à toute personne intéressée par la question de lire l'intégralité du javadoc de l'annotation (qui ne fait que 30 lignes et regorge d'informations utiles condensées) mais je vais extraire quelques gemmes pertinentes à ma situation.
de Type d'annotation WebMvcTest
L'utilisation de cette annotation désactivera la configuration automatique complète et n'appliquera à la place que la configuration pertinente aux tests MVC (c'est-à-dire
@Controller
,@ControllerAdvice
,@JsonComponent
Filtre,WebMvcConfigurer
etHandlerMethodArgumentResolver
beans mais pas@Component
,@Service
ou@Repository
des haricots). [...] Si vous cherchez à charger la configuration complète de votre application et à utiliser MockMVC, vous devriez envisager@SpringBootTest
combiné avec@AutoConfigureMockMvc
plutôt que cette annotation .
Et en fait, seulement @SpringBootTest
+ @AutoConfigureMockMvc
a résolu mon problème, toutes les autres approches utilisant @WebMvcTest
n'a pas pu charger certains des beans requis.
Je reprends mon commentaire que j'ai fait sur la documentation de Spring, car je ne savais pas qu'une tranche était implicite quand on utilise un @WebMvcTest
; en fait, la documentation des tranches MVC indique clairement que toutes les applications ne sont pas chargées, ce qui est par la nature même d'une tranche.
tranche de test personnalisée avec Spring Boot 1.4
Le découpage de test consiste à segmenter le ApplicationContext créé pour votre test. En règle générale, si vous souhaitez tester un contrôleur à l'aide de MockMvc, vous ne voulez certainement pas vous soucier de la couche de données . Au lieu de cela, vous voudrez probablement vous moquer du service utilisé par votre contrôleur et valider que toutes les interactions liées au Web fonctionnent comme prévu.
Au lieu de câbler automatiquement MockMvc, j'ai instancié un objet mockmvc dans une phase de configuration comme celle-ci.
protected void setUp() {
mvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
}