UnsatisfiedDependencyException in a #WebMvcTest test class - java

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", "john.connor#sky.net")
.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.

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.

Related

Spring Boot WebFluxTest test, failed to instantiate repository, specified class is an interface

I am writing the integration tests with #WebFluxTest for my #RestController.
Here are my classes:
#RestController
#RequestMapping("/usager")
public class UsagerController {
#Autowired
private UsagerService usagerService;
#GetMapping
public Usager getUsager() {
return usagerService.create();
}
}
#Service
public class UsagerService implements CrudService<Usager, Integer> {
#Autowired
private UsagerRepository usagerRepository;
#Override
public JpaRepository<Usager, Integer> getRepository() {
return usagerRepository;
}
#Override
public Usager create() {
return new Usager();
}
}
#Repository
public interface UsagerRepository extends JpaRepository<Usager, Integer>, JpaSpecificationExecutor<Usager> {
}
#ExtendWith(SpringExtension.class)
#WebFluxTest(UsagerController.class)
#Import({ UsagerService.class, UsagerRepository.class })
#Tag(TestCase.INTEGRATION)
public class UsagerControllerIT {
#Autowired
private WebTestClient wtc;
#Test
public void getUsager_returnUsager() {
ResponseSpec rs = wtc.get().uri("/usager").exchange();
rs.expectStatus().isOk();
rs.expectHeader().contentType(MediaType.APPLICATION_JSON);
rs.expectBody(Usager.class).isEqualTo(new Usager());
}
}
I get the following exception:
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.dsi.bibliosys.biblioback.repository.UsagerRepository]: Specified class is an interface
I don't understand why Spring can't inject the repository.
Does somebody have an idea ?
I tried another approach using #SpringBootTest. Here is my new test class :
#ExtendWith(SpringExtension.class)
#SpringBootTest
#Tag(TestCase.INTEGRATION)
public class UsagerController02IT {
#Autowired
private UsagerController usagerController;
#Test
public void getUsager_returnUsager() {
WebTestClient wtc = WebTestClient.bindToController(usagerController).build();
ResponseSpec rs = wtc.get().uri("/usager").exchange();
rs.expectStatus().isOk();
rs.expectHeader().contentType(MediaType.APPLICATION_JSON);
rs.expectBody(Usager.class).isEqualTo(new Usager());
}
}
I get this exception:
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'com.dsi.bibliosys.biblioback.controller.UsagerController': Unsatisfied dependency expressed through field 'usagerService'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.dsi.bibliosys.biblioback.service.entity.UsagerService' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
I don't understand why UserService is not available in the application context.
Thanks for your help.
This looks very similar to this. I'd suggest investigating your test configuration and adding it if appropriate.
A quote from Spring on #WebFluxTest
Using this annotation will disable full auto-configuration and instead apply only configuration relevant to WebFlux tests (i.e. #Controller, #ControllerAdvice, #JsonComponent, Converter/GenericConverter, and WebFluxConfigurer beans but not #Component, #Service or #Repository beans).

No qualifying bean of type 'Package.TestDaoRepo' available: expected at least 1 bean which qualifies as autowire candidate

Hey I am using spring boot version 2.3.4 for my web application. I am writing a JUNIT Integration test case for testing my controller, service and Repo using WebMvcTest.
#ExtendWith(SpringExtension.class)
#WebMvcTest(TestCotroller.class)
class ITTestController {
#Test
void test() {
System.out.println("Test me ");
// URL to call. controller and rest of the logic
}
}
I am looking for minimum configuration to load so i have used #WebMvcTest.
This is my controller need to be tested
#RestController
#RequestMapping(value = APIUrlConstants.VERSION + APIUrlConstants.TEST_CONTROLLER)
public class TestCotroller {
#Autowired
TestServiceInf testServiceInf;
#RequestMapping(value = APIUrlConstants.TEST_DB, method = RequestMethod.GET)
public ResponseEntity<String> testDbStatus() {
Long count = testServiceInf.testDbStatus();
String responseMessage = "DB is up and running, total number of products count are -- " + count;
return new ResponseEntity<String>(responseMessage, HttpStatus.OK);
}
#RequestMapping(value = APIUrlConstants.TEST_APPLICATION, method = RequestMethod.GET)
public ResponseEntity<String> testApplicationStatus() {
String responseMessage = testServiceInf.testApplication();
return new ResponseEntity<String>(responseMessage, HttpStatus.OK);
}
}
The Service class
#Service
public class TestServiceImpls implements TestServiceInf {
#Autowired
TestDaoRepo testDaoRepo;
private static String responseMessage = "Application is up and running";
#Override
public Long testDbStatus() {
Long countProduct = testDaoRepo.count();
System.out.println(countProduct);
return countProduct;
}
#Override
public String testApplication() {
return responseMessage;
}
}
Repo class
#Repository
public interface TestDaoRepo extends JpaRepository<CpcMasterProduct, Long> { }
This is the error i am facing -
No qualifying bean of type 'package.TestDaoRepo' available: expected
at least 1 bean which qualifies as autowire candidate. Dependency
annotations:
{#org.springframework.beans.factory.annotation.Autowired(required=true)}
Please help me where i am doing wrong ?
#WebMvcTest won't instantiate #Repository beans. This is intentional in order to make the tests more lightweight and isolate the controller tests. Typically, you would replace your services with mocks for such tests.
See the docs for explanation.
In your case, your test would contain something like:
#ExtendWith(SpringExtension.class)
#WebMvcTest(TestCotroller.class)
class ITTestController {
#MockBean
private TestServiceInf serviceMock;
#Test
void test() {
System.out.println("Test me ");
// URL to call. controller and rest of the logic
}
}
If you want to do integration tests, you shouldn't use #WebMvcTest.

Can't Autowire Service in JUnit 4 Test in Spring Boot Multi Module Application

I have following project structure:
-module1
--src/main/java/at.flobau.demo.module1
---model
----Product.java
---service
----ProductService.java
---TestConfiguration.java
--src/test/java/at.flobau.demo.module1.service
---ProductServiceTest.java
-module2
--src/main/java/at.flobau.demo.main
---MainApplication.java
The Application class looks like this:
#SpringBootApplication(scanBasePackages = {"at.flobau.demo.main"})
#PropertySource(value = "classpath:application.properties")
#EnableJpaRepositories("at.flobau.demo.module1")
#EntityScan(basePackages = {"at.flobau.demo.module1"})
public class PocApplication {
public static void main(String[] args) {
SpringApplication.run(PocApplication.class, args);
}
}
The Service looks like this:
#Service
public class ProductService implements IProductService {
#Autowired
private IProductRepository productRepository;
...
}
The test Class looks like this:
#SpringBootTest
#ContextConfiguration(classes = { TestConfiguration.class }, loader =
AnnotationConfigContextLoader.class)
#RunWith(SpringRunner.class)
public class ProductServiceTest {
#Autowired
private ProductService productService;
...
}
The test configuration file looks like this:
#Configuration
#ComponentScan("at.flobau.demo")
public class TestConfiguration{ }
IntelliJ tells me, that the ProductService inside the test cannot be autowired. When i do run the test i get an exepction:
Error creating bean with name 'ProductServiceTest': Unsatisfied dependency expressed through field
'productService'; nested exception is
org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type
'at.flobau.demo.module1.products.service.ProductService' available: expected at least 1 bean which
qualifies as autowire candidate. Dependency annotations:
{#org.springframework.beans.factory.annotation.Autowired(required=true)}
You should avoid using field injection (even though is possible) and use constructor injection. This will solve this problem as you will be able to pass the service from the constructor but it will also be useful in the future as you can locate usages and trace objects in your code way better than field injections which are "hidden"
So I recommend instead of trying to solve your problem here to refactor your class in constructor injection and pass the service from there either by directly creating the object in your test or by creating a configuration for your test that will generate the object and give the arguments it requires
something like
#ContextConfiguration(classes = { GeneralTester.TestConfig.class })
#RunWith(SpringRunner.class)
public class GeneralTester {
#TestConfiguration
public static class TestConfig {
#Bean
public IProductService productService(final IProductRepository productRepository){
return new ProductService(productRepository);
}
#Bean
public IProductRepository productRepository(){
return mock(IProductRepository.class);
}
}
#Autowire
public IProductService productService;
#Autowire
public IProductRepository productRepository;
#Before
public void setUp() {
reset(productRepository);
}
#After
public void tearDown() {
verifyNoMoreInteractions(productRepository);
}
#Test
public void doSmth() {
//... your setup
when(productRepository.save(any())).thenReturn("something");
//... your call and assertions
verify(productRepository).save(any());
}
}
You can annotate your test class with #SpringBootTest(classes = ProductService.class)
Have you tried creating a bean of the service in the test configuration class?
#TestConfiguration
#ComponentScan("at.flobau.demo")
public class TestConfiguration {
#Bean
public ProductService productService() {
return new ProductService();
}
}

Cannot Autowire Qualifier tagged RestTemplate from unit test

I'm using an remote api in my web application. Some endpoints require an authentication header in the API I'm using. Because of this need I have created two different RestTemplateBuilder bean in my configuration class.
#Configuration
public class RestTemplateConfiguration {
#Bean
#DependsOn(value = {"secureRestTemplateCustomizer"})
#Qualifier("secureRestTemplateBuilder")
public RestTemplateBuilder secureRestTemplateBuilder() {
return new RestTemplateBuilder(secureRestTemplateCustomizer());
}
#Bean
#DependsOn(value = {"publicRestTemplateCustomizer"})
#Qualifier("publicRestTemplateBuilder")
public RestTemplateBuilder publicRestTemplateBuilder() {
return new RestTemplateBuilder(publicRestTemplateCustomizer());
}
#Bean
#Qualifier("secureRestTemplateCustomizer")
public SecureRestTemplateCustomizer secureRestTemplateCustomizer() {
return new SecureRestTemplateCustomizer();
}
#Bean
#Qualifier("publicRestTemplateCustomizer")
public PublicRestTemplateCustomizer publicRestTemplateCustomizer() {
return new PublicRestTemplateCustomizer();
}
}
And these are my custom RestTemplateCustomizers
#Component
public class SecureRestTemplateCustomizer implements RestTemplateCustomizer {
#Override
public void customize(RestTemplate restTemplate) {
restTemplate.setErrorHandler(new ErrorHandler());
restTemplate.getInterceptors().add(new AuthorizationHeaderInterceptor());
}
}
PublicRestTemplateCustomizer
#Component
public class PublicRestTemplateCustomizer implements RestTemplateCustomizer {
#Override
public void customize(RestTemplate restTemplate) {
restTemplate.setErrorHandler(new ErrorHandler());
}
}
There is no problem when I want to use these RestTemplateBuilders in my api clients like below. Spring can autowire them into my api client constructor.
private RestTemplate restTemplate;
#Autowired
public LoginApiClient(#Qualifier("publicRestTemplateBuilder") RestTemplateBuilder restTemplateBuilder) {
this.restTemplate = restTemplateBuilder.build();
}
But in my unit tests this usage is firing an error like
Error creating bean with name 'loginApiClient' defined in file [..\LoginApiClient.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.springframework.boot.web.client.RestTemplateBuilder' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {#org.springframework.beans.factory.annotation.Qualifier(value=publicRestTemplateBuilder)}
#RunWith(SpringRunner.class)
#RestClientTest({LoginApiClient.class})
#Category(IUnitTest.class)
public class LoginApiClientTest {
#Autowired ILoginApiClient loginApiClient;
#Autowired private MockRestServiceServer server;
#Test
public void validateToken_returns_true_for_valid_token() throws Exception{
String token = "token";
this.server.expect(requestTo(this.validateTokenUrl))
.andExpect(method(HttpMethod.POST))
.andRespond(withSuccess(objectMapper.writeValueAsString(validTokenResponse(token)), MediaType
.APPLICATION_JSON));
Boolean isValid = loginApiClient.validateToken(token);
server.verify();
assertThat(isValid,is(equalTo(true)));
}
}
How can I mock these RestTemplates truly and use in my unit test.
I think you are missing the #ComponentScan(value = "your.package") in your Main application class
You do not have context specified.
Try adding this annotation to uour test class:
#ContextConfiguration(classes=RestTemplateConfiguration.class, loader=AnnotationConfigContextLoader.class)
See this article for details: spring-3-1-m2-testing-with-configuration-classes-and-profiles - it's for Spring 3, but newer versions of Spring work similarly.
Here's a newer tutorial: junit-spring-integration-example

Unable to test spring rest controller because of NoSuchBeanDefinitionException

I'm writing a test in which I want to test the rest controller.
I'm mocking the service which is in the controller, but spring wants also the repository which is inside the service ...
#RunWith(SpringRunner.class)
#WebMvcTest(AccountRestController.class)
public class AccountRestControllerTest {
#Autowired
private MockMvc mockMvc;
#MockBean
private PremiumAccountService service;
AccountRequestResource resource = new AccountRequestResource(25d);
PremiumAccount premiumAccount = new PremiumAccount("abc", "cba", LocalDateTime.now(), 25d);
#Test
public void testGetAccountWithAvailableGB() throws Exception {
given(service.getAccountByGB(25d)).willReturn(Optional.of(premiumAccount));
mockMvc.perform(post("/api/account"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.login", is("abc")));
}
}
Stack :
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'premiumaccountservice.repository.PremiumAccountRepository' available: expected at least 1 bean which qualifies as autowire candidate.
AccountRestController :
#RestController
#RequestMapping("/api")
public class AccountRestController {
private final PremiumAccountService service;
#Autowired
public AccountRestController(PremiumAccountService service) {
this.service= service;
}
}
It looks like you forgot to add #Autowired annotation on your service instance variable in rest controller.

Categories

Resources