I have a service which uses an autowired instance of RestTemplate like below
#Service
class SomeAPIService {
private RestTemplate restTemplate;
SomeAPIService(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
this.restTemplate.setRequestFactory(HttpUtils.getRequestFactory());
}
}
Everything runs fine in non-test environment. But when I try to run following unit test in test profile, it starts complaining about unable to autowire rest template.
#RunWith( SpringJUnit4ClassRunner.class )
#SpringBootTest(classes = MyApplication.class, webEnvironment = RANDOM_PORT, properties = "management.port:0")
#ActiveProfiles(profiles = "test")
#EmbeddedPostgresInstance(flywaySchema = "db/migration")
public abstract class BaseTest {
}
#SpringBootTest(classes = SomeAPIService.class)
public class SomeAPIServiceTest extends BaseTest {
#Autowired
SomeAPIService someAPIService;
#Test
public void querySomeAPI() throws Exception {
String expected = someAPIService.someMethod("someStringParam");
}
}
Following is the detailed exception -
Caused by:
org.springframework.beans.factory.UnsatisfiedDependencyException:
Error creating bean with name 'someAPIService': Unsatisfied dependency
expressed through constructor parameter 0; nested exception is
org.springframework.beans.factory.NoSuchBeanDefinitionException: No
qualifying bean of type 'org.springframework.web.client.RestTemplate'
available: expected at least 1 bean which qualifies as autowire
candidate. Dependency annotations: {}
Any clues?
Following helped me get the correct dependencies autowired. The solution is to also include RestTemplate.class in list of classes given to SpringBootTest.
#SpringBootTest(classes = {RestTemplate.class, SomeAPIService.class})
class SomeAPIService {
#Autowired
SomeAPIService someAPIService;
#Test
public void querySomeAPI() throws Exception {
String expected = someAPIService.someMethod("someStringParam");
}
}
#Emre answer was helpful in guiding me towards the final solution.
You are trying to autowire SomeAPIService without satisfying its dependencies. You should inject Rest Template to SomeAPIService. But you are getting NoSuchBeanDefinitionException for Rest Template.
Take a look how to inject it :
How to autowire RestTemplate using annotations
Alternative answer would be - to use TestRestTemplate
From official docs >>>
TestRestTemplate can be instantiated directly in your integration tests, as shown in the following example:
public class MyTest {
private TestRestTemplate template = new TestRestTemplate();
#Test
public void testRequest() throws Exception {
HttpHeaders headers = this.template.getForEntity(
"https://myhost.example.com/example", String.class).getHeaders();
assertThat(headers.getLocation()).hasHost("other.example.com");
}
}
Alternatively, if you use the #SpringBootTest annotation with WebEnvironment.RANDOM_PORT or WebEnvironment.DEFINED_PORT, you can inject a fully configured TestRestTemplate and start using it. If necessary, additional customizations can be applied through the RestTemplateBuilder bean.
Related
I have a problem that prevents me from moving forward and I don't know how to solve it. I have a class called Validator where I store validation functions and I need to do unit tests for each one and validate its functionality, but there are some of them that use the Spring Environment instance in which it accesses the properties file. If I do "normal" unit tests, whenever I call the function where this feature is implemented, it returns me Environment is null. I have tried using #Autowired instead of instantiating with new in the test class, the use of #RunWith(SpringRunner.class) and since the test classes and the function classes are in different packages I have also used the #ComponentScan and it gives me an error... What am I doing wrong or what am I doing wrong?
I have the code of the Validator class in this way:
#Component
public class Validator {
#Autowired
public Environment env;
public CodRespuestaWS validateTypeOperation(TypeOperation typeOperation, String operation) {
String response = env.getProperty(typeOperation.toString() + "." + operation);
if (OK.equalsIgnoreCase(response)) {
return new CodResponseWS();
}
return new CodResponseWS(CodResponseWS.ER100, typeOperation.toString()+" not allowed:" + operation);
}
}
And in the test I do it this way:
#ComponentScan(basePackages = "com.functions.validators")
#RunWith(SpringRunner.class)
public class ValidateRequestHigh {
RequestHigh requestHigh = new RequestHigh();
CodResponseWS response;
#Autowired Validator validator;
HighValidator highValidator = new HighValidator();
UserData userData = new UserData();
#Test
public void test() throws Exception {
response = validator.validateTypeOperation(TypeOperation.typeOperationHigh, "high");
System.out.println(response.getCodResponse());
}
}
The problem that I mentioned before the NULL, is that when executing the test it did not even reach the print that I have set, but rather it stayed on the line
String response = env.getProperty(typeOperation.toString() + "." +
operation);
And it marked the error that env was NULL and now it returns a different one
And the error that returns me now is:
org.springframework.beans.factory.UnsatisfiedDependencyException:
Error creating bean with name
'functiontest.unittest.ValidateRequestHighTest': Unsatisfied
dependency expressed through field 'validator'; nested exception is
org.springframework.beans.factory.NoSuchBeanDefinitionException: No
qualifying bean of type 'com.functions.validators.Validator'
available: expected at least 1 bean which qualifies as autowire
candidate. Dependency annotations:
{#org.springframework.beans.factory.annotation.Autowired(required=true)}
I guess it's already understood, but I'll detail it more just in case, the functions are in
src/main/java/com.functions.validators/Validators.java
and the tests
src/main/test/functiontest.unittest/ValidateRequestHighTest.java
I am using version 5.1.2.RELEASE of SpringBoot and JUnit 4
Using field injection makes Validator class impossible to be unit tested because it provides no way to pass the Environment instance to it. but you can spin up the whole application context and configure it to do dependency injection stuff using #SpringBootTest which is not a unit test but integration test.
if I do "normal" unit tests, whenever I call the function where this
feature is implemented, it returns me Environment is null.
Because when you use new to instantiate a bean its not a Spring managed bean anymore and its dependencies are not resolved.
I have tried using #Autowired instead of instantiating with new in the
test class, the use of #RunWith(SpringRunner.class) and since the test
classes and the function classes are in different packages I have also
used the #ComponentScan and it gives me an error
You should also add SpringBootTest annotation for loading into application context.
If you want to really unit test your class you don't need application context instead do the following
Change the Validator class like:
#Component
public class Validator {
private Environment env;
public Validator(Environment env) {
this.env = env;
}
....
}
And for unit testing it do:
public class ValidatorTest {
Validator validator;
MockEnvironment environment;
#Before
public void setUp() {
environment = new MockEnvironment();
environment.setProperty("key1", "value1");
//...
environment.setProperty("keyn", "valuen");
validator = new Validator(environment);
}
#Test
public void test() {
// test stuff
}
}
Hi I am using MongoDB transaction feature with Spring Boot 2.3.5.RELEASE version. I am getting the following exception. I am writing the integration test with the controller classes.
Exception given below.
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'com.app.cat.resource.Test0': Unsatisfied dependency expressed through field 'controller'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.dell.pde.resources.NVTValidationCategoryController' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:643) ~[spring-beans-5.2.10.RELEASE.jar:5.2.10.RELEASE]
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:130) ~[spring-beans-5.2.10.RELEASE.jar:5.2.10.RELEASE]
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPost
I provide below the code snippet.
#Profile("test")
#ActiveProfiles("test")
#DataMongoTest(excludeAutoConfiguration = MongoDBTxnConfiguration.class)
#Import(TestMongoConfig1.class)
#EnableAutoConfiguration(exclude={ SecurityAutoConfiguration.class, DataSourceAutoConfiguration.class, RedisAutoConfiguration.class,
RedisRepositoriesAutoConfiguration.class,
SecurityFilterAutoConfiguration.class, SecurityAutoConfiguration.class })
#SpringBootApplication(scanBasePackages = {"com.app.cat"})
public class Test0 {
#Autowired
private CategoryController controller;
#Autowired
#Qualifier("validations")
private ValidationService vldnService;
#Test
void testAllValidationsBeforeEntry() {
System.out.println("controller : " + vldnService);
assertEquals(true, true);
}
}
Please guide me what I am doing wrong. Please help me.
#DataMongoTest annotation also uses #AutoConfigureDataMongo, #ImportAutoConfiguration. If you provide autoconfigured classes, it will conflict. You have to use #DataMongoTest to exclude other classes to load and run. Since it is a Test class, do not use #SpringBootApplication(scanBasePackages = {"com.app.cat"}). Finally I provide the bit code snippet which may be helpful to you.
#DataMongoTest(excludeAutoConfiguration = {SecurityAutoConfiguration.class,
SecurityFilterAutoConfiguration.class,
SecurityAutoConfiguration.class, ... <any other configuration>})
#Profile("test")
#ComponentScan(basePackages = {"com.app.cat"}, excludeFilters={
#ComponentScan.Filter(type=FilterType.ASSIGNABLE_TYPE,
value= {MongoDBTxnConfiguration.class,SecurityCloudConfig.class})})
public class Test {
#Autowired
private YourControler ctrlr;
#Test
void test1() {
// Logic for testing
}
}
Here is the class I want to test
#Component
public class PermissionCheck {
#Autowired
private MyEntityRepository myEntityRepository;
public boolean hasPermission(int myEntityID) {
MyEntity myEntity = myEntityRepository.findById(myEntityId);
return myEntity != null;
}
}
Here is the test class
#RunWith(SpringRunner.class)
public class PermissionCheckTests {
#MockBean
private MyEntityRepository myEntityRepository;
private PermissionCheck permissionCheck;
#Before
public void before() {
this.permissionCheck = new PermissionCheck();
}
#Test
public void shouldHasPermission() {
MyEntity myEntity = new MyEntity();
when(this.myEntityRepository.findById(any())).thenReturn(myEntity);
assertTrue(this.permissionCheck.hasPermission(0));
}
}
And when I run this test I got
java.lang.NullPointerException
at PermissionCheck.hasPermission(PermissionCheck.java:line1)
at PermissionCheckTests.shouldHasPermission(PermissionCheckTests.java:line2)
In the above, line1 and line2 refer to these two lines
MyEntity myEntity = myEntityRepository.findById(myEntityId);
assertTrue(this.permissionCheck.hasPermission(0));
And using debugger I see that when entering PermissionCheck.hasPermission from PermissionCheckTests.shouldHasPermission, the repository field
#Autowired
private MyEntityRepository myEntityRepository;
is null.
I created these classes by referring to others existing codes, from different places, and without really understanding how to the annotations (partially due to running out of time), so if someone can tell me not only how to fix, but also why I'm wrong, I would really appreciate it!
Edit:
I made the change suggested by #Nikolas Charalambidis (thank you!), so my PermissionCheck class now looks exactly like
#RunWith(SpringRunner.class)
public class PermissionCheckTests {
#Autowired // you need to autowire
private PermissionCheck permissionCheck; // and it uses #MockBean dependency
#MockBean // if no such #MockBean exists
private MyEntityRepository myEntityRepository; // the real implementation is used
#Test
public void shouldHasPermission() {
MyEntity myEntity = new MyEntity();
when(this.myEntityRepository.findById(any())).thenReturn(myEntity);
assertTrue(this.permissionCheck.hasPermission(0));
}
}
But I then got the following exception
org.springframework.beans.factory.UnsatisfiedDependencyException:
Error creating bean with name 'PermissionCheckTests':
Unsatisfied dependency expressed through field 'permissionCheck';
nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException:
No qualifying bean of type 'PermissionCheck' available:
expected at least 1 bean which qualifies as autowire candidate.
Dependency annotations:
{#org.springframework.beans.factory.annotation.Autowired(required=true),
#org.springframework.beans.factory.annotation.Qualifier(value="")}
With a little bit search, from this SO answer, I feel like I should not #Autowired PermissionCheck, since it is a class, not an interface.
Does that mean I have to create an interface, #Autowired it and let PermissionCheck implement it? It seems redundant to me, since I don't see why I need such an interface. Is there a way to make it work without creating a new interface which I will solely use for this purpose?
The instance of class PermissionCheck is not properly injected. The tests use the Spring container in the same way as the production code. The following line would not inject the myEntityRepository.
this.permissionCheck = new PermissionCheck();
Spring: NullPointerException occurs when including a bean partly answers your question. You need to #Autowire the thing.
The Spring test context is no different. As long as #MockBean MyEntityRepository exists as a mocked bean, the PermissionCheck will be autowired in the standard way using the mocked class in precedence over the existing bean in the test scope.
#RunWith(SpringRunner.class)
public class PermissionCheckTests {
#Autowired // you need to autowire
private PermissionCheck permissionCheck; // and it uses #MockBean dependency
#MockBean // if no such #MockBean exists
private MyEntityRepository myEntityRepository; // the real implementation is used
#Test
public void shouldHasPermission() {
MyEntity myEntity = new MyEntity();
when(this.myEntityRepository.findById(any())).thenReturn(myEntity);
assertTrue(this.permissionCheck.hasPermission(0));
}
}
in my case i was using lombok i was added #NoArgsConstructor
to the class that that i had injected my repository.
#Component
#AllArgsConstructor
#NoArgsConstructor // what cuased the null pointer
public class ParcelUtil {
private final Logger log = LoggerFactory.getLogger(OrderEventConsumer.class);
private ParcelRepository parcelRepository;
}
my test:
#SpringBootTest
#DirtiesContext
public class OrderEventConsumerTest extends SpringBootEmbeddedKafka {
#MockBean
private ParcelRepository parcelRepository;
}
#Test
public void update_parcel_inride_to_delivered_nxb() throws JsonProcessingException {
//given
List<Parcel> parcels = DataUtil.createParcel_inride_and_draft_nxb();
Mockito
.when(parcelRepository.findFirstByParcelGroupId("12"))
.thenReturn(Optional.ofNullable(parcels.get(0))); //where i got null pointer
}
}
I have two #Configuration classes. I need a bean from one configuration class to another. I have autowired the configuration 1 into 2. All works fine. When executing the unit testing, am getting the below exception.
setUpContext(com.trafigura.titan.framework.services.messaging.loader.SpringLoadTest)
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'com.xxx.MessagingServicesConfig': Injection of autowired dependencies failed;
nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.xxx.EMSJMSConfig com.xxx.MessagingServicesConfig.emsJmsConfig;
nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type[com.xxx.EMSJMSConfig] found for dependency:
expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
Is there anything I need to do additionally to make this working?
Below is the setup for testing.
#Configuration
#Import({MessagingServicesConfig.class,...,EMSJMSConfig.class
})
public class MessagingConfig {}
#Profile("EMS-MESSAGING")
#Configuration
public class EMSJMSConfig {
#Bean
public javax.jms.ConnectionFactory jmsSubscriberConnectionFactory() throws JMSException {
SingleConnectionFactory singleConnectionFactory = new SingleConnectionFactory(tibjmsConnectionFactory());
return singleConnectionFactory;
}
}
#Configuration
public class MessagingServicesConfig {
#Autowired
private EMSJMSConfig emsJmsConfig;
#Bean(destroyMethod = "shutdown")
public MessagingService messagingService() throws JMSException {
...
ConnectionFactory cf=emsJmsConfig.jmsSubscriberConnectionFactory(); // Getting NPE at this line.
}
}
and finally the test class,
public class MessagingServicesConfigTest {
private MessagingServicesConfig config;
private EMSJMSConfig emsJmsConfig;
#BeforeMethod
public void setUp() throws Exception {
config = new MessagingServicesConfig();
... //what needs to be done here to have the EMSJMSConfig
}
#Test
public void testBuildsCorrectService() throws JMSException {
MessagingService service = config.messagingService();
...
}
}
By calling new you're creating object yourself, Spring doesn't know anything about it.
Moreover, you should have a test configuration which will be aware of your beans.
Use an appropriate Runner to load SpringContext.
#ContextConfiguration(classes = TestConfig.class)
#RunWith(SpringRunner.class)
class Tests {
#Autowired // if needed
private MessagingServicesConfig config;
}
While in TestConfig you can create beans or import configuration from the Application:
#Configuration
#Import({MessagingServicesConfig.class})
public class TestConfig {}
#Configuration
#Import({EMSJMSConfig.class})
public class MessagingServicesConfig {}
Or you can refer to your config classes directly:
#ContextConfiguration(classes = {MessagingServicesConfig.class, EMSJMSConfig.class})
I am trying to write a test for my controller. When the web service is running everything works fine. However, when I run the test I get:
Error creating bean with name 'Controller': Unsatisfied dependency expressed through field 'service'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.prov.Service' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
As you can see below, I believe have everything correctly Autowired and my project structure is correctly set up so that the component scanner can find the annotations properly, yet I still get this error.
Controller:
#RestController
#RequestMapping("/api")
public class Controller {
#Autowired
private Service service;
#JsonView(Views.All.class)
#RequestMapping(value = "/prov/users", method = RequestMethod.POST)
#ResponseBody
public CommonWebResponse<String> handleRequest(#RequestBody UserData userData) {
return service.prov(userData);
}
}
Service:
#Service
public class Service {
#Autowired
private Repo repo;
#Autowired
private OtherService otherService;
public CommonWebResponse<String> prov(UserData userData) {
// do stuff here
return new SuccessWebResponse<>("Status");
}
}
Controller Test:
#RunWith(SpringRunner.class)
#WebMvcTest(
controllers = Controller.class,
excludeFilters = {
#ComponentScan.Filter(
type = FilterType.ASSIGNABLE_TYPE,
value = {CorsFilter.class, AuthenticationFilter.class}
)
}
)
#AutoConfigureMockMvc(secure = false)
public class ControllerTest {
public static final MediaType APPLICATION_JSON_UTF8 = new MediaType(MediaType.APPLICATION_JSON.getType(), MediaType.APPLICATION_JSON.getSubtype(), Charset.forName("utf8"));
#Autowired
private MockMvc mvc;
#Test
public void connectToEndpoint_shouldReturnTrue() {
UserData userData = new UserData("a", "bunch", "of", "fields");
try {
mvc.perform(post("/api/prov/users").contentType(APPLICATION_JSON_UTF8)
.content(asJsonString(userData))
.accept(MediaType.ALL))
.andExpect(status().isOk());
} catch (Exception e) {
Assert.fail();
}
}
}
The Controller class autowires your Service class. Therefore, testing the Controller class requires existence of your Service class because Controller depends on creating a bean of type Service. This means you either have to #Autowired your service class into your test, or (preferably) mock it using something like Mockito.
(edit with code example):
#RunWith(SpringRunner.class)
#WebMvcTest(Controller.class)
public class ControllerTest {
#MockBean
private Service service
#Autowired
private MockMvc mvc;
#Test
public void foo() {
String somePayload = "Hello, World";
String myParams = "foo";
when(service.method(myParams)).thenReturn(somePayload);
mvc.perform(get("my/url/to/test").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("$", is(equalTo("Hello, World"))));
}
}
Do note that this example uses Hamcrest for things like is() and equalTo()