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.
Related
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).
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
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.
I am creating application in Spring Boot. I created service that looks that way:
#Service
public class MyService {
#Value("${myprops.hostname}")
private String host;
public void callEndpoint() {
String endpointUrl = this.host + "/endpoint";
System.out.println(endpointUrl);
}
}
This service will connect to REST endpoint to other application (developed also by me) that will be deployed along. That is why I want to customize hostname in application.properties file (-default, -qa, -dev).
My application builds and works just fine. I tested it by creating controller that calls this service, and it populates host field with correct property from application.properties.
The problem occurs when I try to write test for this class.
When I try this approach:
#RunWith(SpringRunner.class)
public class MyServiceTest {
#Autowired
private MyService myService;
#Test
public void callEndpoint() {
myService.callEndpoint();
}
}
i receive exception:
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'com.ge.bm.wip.comp.processor.service.MyServiceTest': Unsatisfied dependency expressed through field 'myService'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.ge.bm.wip.comp.processor.service.MyService' available: expected at least 1 bean which qualifies as autowire candidate.
And some more nested exception. I can post them if it will help.
I suppose that for some reason SpringRunner does not launch this test in Spring context and therefore cannot see bean MyService.
Does anyone know how it can be fixed? I tried normal initialization:
private MyService myService = new myService();
but then host field is null
You have to annotate your test with #SpringBootTest as well.
Try:
#SpringBootTest
#RunWith(SpringRunner.class)
public class MyServiceTest {
#Autowired
private MyService myService;
#Test
public void callEndpoint() {
myService.callEndpoint();
}
}
I have a simple spring app with one controller
#RestController
public class UserController {
// #Autowired
// UserServiceImpl userService;
#RequestMapping(value="/getUser", method = RequestMethod.GET)
public String getUser(){
// return userService.greetUser();
return "Hello user";
}
It works when I start it. If I uncomment #Autowired and run with the first return statement using UserService it also works.
My Service interface
#Service
public interface UserService {
String greetUser();
void insertUsers(List<User> users);
}
and implementation
#Service
public class UserServiceImpl implements UserService{
#Override
public String greetUser() {
return "Hello user";
}
}
But when I test it, the app falls with the following errors
java.lang.IllegalStateException: Failed to load ApplicationContext
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'userController': Unsatisfied dependency expressed through field 'userService'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.example.demo.service.UserServiceImpl' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.example.demo.service.UserServiceImpl' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
The test class
#RunWith(SpringRunner.class)
#WebMvcTest
public class DemoApplicationTests {
#Autowired
private MockMvc mockMvc;
#Test
public void shouldReturnHelloString() throws Exception{
this.mockMvc
.perform(get("/getUser"))
.andDo(print())
.andExpect(status().isOk())
.andExpect(content().string("Hello user"));
}
}
Also, if I remove
// #Autowired
// UserServiceImpl userService;
and run test with second return statement, the test execute without error. I understand that the problem is in the UserServiceImpl, but I don't know what it is. What do I need to correct?
You should try to autowire your bean by an interface, not implementation
#Autowired
UserService userService;
And also you should remove #Service from UserService interface