Issue with testing Spring MVC slice in SpringBoot 1.4

user2693135 picture user2693135 · Jun 28, 2016 · Viewed 20.2k times · Source

I'm trying out the new Spring Boot 1.4 MVC testing features. I have the following controller.

@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";
  }
}

My minimal ProductService implementation is:

@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();
  }

}

The code of ProductRepository is:

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

I'm trying to use the new @WebMvcTest to test the conroller. My view is a thymeleaf teamplate. And my controller test is this:

@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"));               
 }
}

But, on running the test I get this error.

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: {}

I need help to resolve the issue to properly test ProductController. Suggestions for additional andExpect() for more thorough testing of the controller will be highly appreciated.

Thanks in advance.

Answer

Antonio picture Antonio · Nov 16, 2017

Who is interested in loading the full application should try using @SpringBootTest combined with @AutoConfigureMockMvc rather than the @WebMvcTest.

I have been struggling with the problem for quite a while, but finally I got the complete picture.
The many tutorials on the internet, as well as the official Spring documentation I found so far , state that you can test your controllers using @WebMvcTest; that's entirely correct, still omitting half of the story though.
As pointed out by the javadoc of such annotation, @WebMvcTest is only intended to test your controllers, and won't load all your app's beans at all, and this is by design.
It is even incompatible with explicit bean scanning annotations like @Componentscan.

I suggest anybody interested in the matter, to read the full javadoc of the annotation (which is just 30 lines long and stuffed of condensed useful info) but I'll extract a couple of gems relevant to my situation.

from Annotation Type WebMvcTest

Using this annotation will disable full auto-configuration and instead apply only configuration relevant to MVC tests (i.e. @Controller, @ControllerAdvice, @JsonComponent Filter, WebMvcConfigurer and HandlerMethodArgumentResolver beans but not @Component, @Service or @Repository beans). [...] If you are looking to load your full application configuration and use MockMVC, you should consider @SpringBootTest combined with @AutoConfigureMockMvc rather than this annotation.

And actually, only @SpringBootTest + @AutoConfigureMockMvc fixed my problem, all other approaches that made use of @WebMvcTest failed to load some of the required beans.

EDIT

I take back my comment I made about Spring documentation, because I wasn't aware that a slice was implied when one uses a @WebMvcTest; actually the MVC slice documentation put it clear that not all the app is loaded, which is by the very nature of a slice.

Custom test slice with Spring Boot 1.4

Test slicing is about segmenting the ApplicationContext that is created for your test. Typically, if you want to test a controller using MockMvc, surely you don’t want to bother with the data layer. Instead you’d probably want to mock the service that your controller uses and validate that all the web-related interaction works as expected.