Talgat
Talgat

Reputation: 33

UnsatisfiedDependencyException in a @WebMvcTest test class

My unit tests fail after I added @Service to the controller. The project is Spring-boot v 2.0.1.RELEASE. I've spent many hours trying to find the answer but no luck. The test worked before I added @Service annotation and my service class has a repository in it.

Stack trace:

2018-04-24 12:57:12.487 WARN 940 --- [ main] o.s.w.c.s.GenericWebApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'fleetController': Unsatisfied dependency expressed through field 'service'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'uk.co.vw.lead.service.ContactUsService' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}

Controller:

@Slf4j
@RestController
@RequestMapping(value = VERSION, consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
public class FleetController {
public static final String VERSION = "1.0";

@Autowired
private ContactUsService service;

@InitBinder
public void initBinder(final WebDataBinder webDataBinder) {
    webDataBinder.registerCustomEditor(NatureOfEnquiryEnum.class, new  NatureOfEnquiryEnumConverter());
    webDataBinder.registerCustomEditor(FleetSizeEnum.class, new  FleetSizeEnumConverter());
}

@PostMapping(value = "/fleet/contact-us")
public ResponseEntity contactUs(@Valid ContactUsDTO formDTO) {
    service.createForm(formDTO);
    return new ResponseEntity(HttpStatus.NO_CONTENT);
}

@PostMapping(value = "/fleet/request-demo")
public ResponseEntity requestDemo(@Valid RequestDemoDTO demoDTO) {



    return new ResponseEntity(HttpStatus.NO_CONTENT);
}

Service:

@Service
public class ContactUsServiceImpl implements ContactUsService {

@Autowired
private FleetRepository repository;

@Override
public void createForm(ContactUsDTO formDTO) {
    ContactUsForm form = populateContactUsForm(formDTO);
    repository.save(form);
}

}

Test class:

@RunWith(JUnitPlatform.class)
@WebMvcTest(FleetController.class)
@ExtendWith(SpringExtension.class)
public class FleetControllerTest {

private final String CONTACT_US_URL = "/fleet/contact-us";

@Autowired
private MockMvc mockMvc;

@MockBean
private FleetRepository repository;

@Autowired
private ContactUsService service;


@Test
public void contactUsSuccessTest() throws Exception {
    this.mockMvc.perform( post("/" + VERSION + CONTACT_US_URL)
                    .contentType(MediaType.APPLICATION_FORM_URLENCODED_VALUE)
                    .param("firstname", "John")
                    .param("lastname", "Doe")
                    .param("company", "Skynet")
                    .param("emailAddress", "[email protected]")
                    .param("telephone", "020 8759 4294")
                    .param("natureOfEnquiry", "new")
                    .param("comments", "some comments")
                    .param("recaptchaResponse", "success"))
            .andExpect(status().isNoContent());
}

@Test
public void contactUsMissingRequiredFieldsTest() throws Exception {
    this.mockMvc.perform( post("/1.0/fleet/contact-us")
            .contentType(MediaType.APPLICATION_FORM_URLENCODED_VALUE))
            .andExpect(status().isBadRequest());
}

}

Please help as I have no idea what is going on.

Upvotes: 3

Views: 5976

Answers (1)

davidxxx
davidxxx

Reputation: 131326

Test classed annotated with @WebMvcTest are tests that focus only on the Spring MVC components : controllers.

So the service field declared in your unit test could not be autowired:

@Autowired
private ContactUsService service;

So you should mock also this dependency :

@MockBean
private ContactUsService service;

Note also that as FleetController doesn't have any direct dependency on FleetRepository, mocking this bean is not required :

@MockBean
private FleetRepository repository;

It is even worse as it adds a mock in the context that could create side effect during your test.
You have to mock only the direct dependencies of the controller under test.


As alternative if you want to mock only some beans and not all which are not controllers, don't use @WebMvcTest and instead of use @SpringBootTest that will load the whole context.
Then declare in the test class, the class(es) you want to mock with @MockBean.

Upvotes: 11

Related Questions