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();
}
}
Related
I have a configuration class like the following
#Configuration
public class Configuration {
#Autowired
private JdbcTemplate jdbcTemplate;
#Bean
SimpleJdbcCall simpleJdbcCall() {
return new SimpleJdbcCall(jdbcTemplate).withProcedureName("");
}
}
I am trying to write a unit test for this configuration class. My test class looks like the following.
#ContextConfiguration(classes = { Configuration.class })
#RunWith(SpringRunner.class)
public class ConfigurationTest {
ApplicationContextRunner context = new ApplicationContextRunner()
.withUserConfiguration(Configuration.class);
#Test
public void should_check_presence_of_example_service() {
context.run(it -> {
assertThat(it).hasSingleBean(SimpleJdbcCall.class);
});
}
}
When I am running the test in the ConfigurationTest class, I am getting an error like the one below.
Error creating bean with name 'Configuration': Unsatisfied dependency
expressed through field 'jdbcTemplate'; nested exception is
org.springframework.beans.factory.NoSuchBeanDefinitionException: No
qualifying bean of type 'org.springframework.jdbc.core.JdbcTemplate'
available: expected at least 1 bean which qualifies as autowire
candidate. Dependency annotations:
{#org.springframework.beans.factory.annotation.Autowired(required=true)}
I tried to solve this by crating a bean jdbcTemplate in the Configuration class and passed datasource. Then the test unit test does not find the bean datasource. After that I used #TestConfiguration in the ConfigurationTest class and crated a mock(jdbcTemplate). That did not work either.
I found a solution for my problem, and I thought it might be helpful for others if someone is in the same situation. Actually my goal was to increase the test coverage because of the company requirement, and the following solution works for me.
I changed my configuration class like below
#Configuration
public class Configuration {
#Bean
SimpleJdbcCall simpleJdbcCall(DataSource dataSource) {
return new SimpleJdbcCall(dataSource).withProcedureName("");
}
}
I had to change my test class like below. Now the test class is not complaining, and I am getting 100% coverage of the Configuration class.
#SpringBootTest
public class ConfigurationTest {
#TestConfiguration
static class MyConfiguration {
#Bean
DataSource dataSource() {
return mock(DataSource.class);
}
}
#Autowired
private DataSource dataSource;
#Autowired
private Configuration configuration;
#Test
public void should_check_presence_of_simpleJdbcCall_Bean() {
SimpleJdbcCall simpleJdbcCall = configuration.simpleJdbcCall(dataSource);
Assertions.assertNotNull(simpleJdbcCall);
}
}
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 have a properly working Spring boot application. in the Spring boot application class i have these 2 beans:
#Bean
public StatsdMeterRegistry graphiteRegistry(StatsdConfig statsdConfig) {
statsdMeterRegistry = StatsdMeterRegistry
.builder(statsdConfig)
.nameMapper(new GraphiteHierarchicalNameMapper(HOST_TAG))
.build();
return statsdMeterRegistry;
}
#Bean
MeterRegistryCustomizer<MeterRegistry> metricsCommonTags() {
InetAddress localHost;
try {
localHost = InetAddress.getLocalHost();
} catch (UnknownHostException e) {
log.error("Failed to get the host name");
return null;
}
return registry -> registry.config()
.commonTags(HOST_TAG, localHost.getHostName() + "_" + SERVICE_NAME);
}
These bean are working fine during run time but fail during test time with:
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'graphiteRegistry' during test.
My test clss looks something like this:
#RunWith(SpringRunner.class)
#WebMvcTest(IntentModelingController.class)
#TestPropertySource("classpath:application-test.yml")
public class IntentModelingControllerTest {
#Autowired
private MockMvc mockMvc;
#MockBean
private IntentModelingService intentModelingService;
#Autowired
private WebApplicationContext webApplicationContext;
private Gson gson = new Gson();
Any advice on how to make these beans visible during test time ?
Edit: if i comment the first bean and leave the second bean as is - the test run well.
Edit#2: The difference between the 1st bean which cause the issue and the 2nd bean which is fine is that the 1st bean requires a parameter:Parameter 0 of method graphiteRegistry in yyy.xxx.intentmodeling.server.IntentModelingApplication required a bean of type 'io.micrometer.statsd.StatsdConfig' that could not be found.
SOLVED by adding 2 fields to the test:
#MockBean
private StatsdMeterRegistry statsdMeterRegistry;
#MockBean
StatsdConfig statsdConfig;
Try importing the configuration class that defines the beans on top of your test class.
#RunWith(SpringRunner.class)
#WebMvcTest(IntentModelingController.class)
#TestPropertySource("classpath:application-test.yml")
#Import(YourConfig.class)
Another possible issue could be that the #Bean is detected, but the graphiteRegistry method throws an exception. This could happen, for example, due to missing properties (I see you defined an application-test.yml, does it contain all the needed properties to instantiate your bean?).
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'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.