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
}
}
Related
In my code i mock an object :
#Mock
ElasticService elasticServiceMock;
#Autowired
ElasticConfiguration elasticConfiguration;
an i try to do a test :
#Test
public void measureChannelProcessor() throws IOException {
when(elasticServiceMock.insert(anyString(),anyString())).thenAnswer(invocation -> {
String index = (String) invocation.getArguments()[0];
String message = (String) invocation.getArguments()[1];
String requestUri = new StringBuilder()
.append(elasticConfiguration.baseRequestBuilder(index))
.toString();
}
when i call elasticConfiguration.baseRequestBuilder(index) i have null.
the real class of elasticConfiguration is this:
#Configuration
public class ElasticConfiguration {
#Autowired ElasticParameters elasticParameters;
public String baseRequestBuilder(String index){
String toRet = new StringBuilder()
.append(elasticParameters.getProtocol())
.append("://")
.append(elasticParameters.getHost())
.append(":")
.append(elasticParameters.getPort())
.append("/")
.append(index)
.append("/")
.append(elasticParameters.getType())
.append("/")
.toString();
return toRet;
}
in particular i want simple real class elasticConfiguration but
#Autowired
ElasticConfiguration elasticConfiguration;
dont work! and i have java.lang.NullPointerException.
the question is how use an object in Test?
Also if i use #Mock ElasticConfiguration elasticConfiguration;
i have same error but with debug i view that #Autowired ElasticParameters
elasticParameters; in public class ElasticConfiguration { is null.
Some tips?
Thanks
Regards
If you have to #autowire the object, then you require some form of IOC container, in your case for Java, this is likely Spring.
Typically, for Spring to work in accordance with your tests and to autowire your beans at the correct time, you will need the correct annotations, to tell spring what to do when you execute your test suite.
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
#ContextConfiguration(classes = ElasticConfiguration.class)
You should also include whichever configuration class contains the bean definition for ElasticParameters in the above statement. So it would look like:
#ContextConfiguration(classes = {ElasticConfiguration.class, OtherConfiguration.class})
Also, if you are using component-scan as a means of automatic bean configuration, then simply you can declare an additional annotation:
#ComponentScan(basePackages = "com.your.package.with.beans")
Generally when we use an IOC container to run our tests, they are integration tests or higher in the testing pyramid. Unit tests do not require it.
I would advise you to read more about writing Integration tests here from the Spring docs. It covers everything you need to know to run your tests correctly.
In Junit5, when using Extensions, we can run BeforeAll and AfterAll methods, and I'm trying to change behaviour of the test using Annotations.
However, I'm using these annotations as #Qualifiers as well for bean initialisation, and want to be able to initialise a bean using the Annotation identified on the test
I wish to programmatically Initialise the Bean on run time using my Qualifier Annotations
I know SpringExtension for Junit5 can get
SpringExtension.getApplicationContext(context).getAutowireCapableBeanFactory()
Using which I can call the bean initialisation factory, but I don't know how to initialise the beans using Annotations which are Qualifiers
I have multiple Beans of same type, identified by Qualifiers
The problem I'm stuck with is
Currently, I'm statically initialising the Type of user credentials using AutoWired and then basis the annotation I'm using the pre-initialized UserCredential using switch case.
The idea is to have a test class #ExtendsWith(ResetPortal.class) and then it indicates, which type of user it can use to Reset (Login before test) with.
I'm using Qualifier Annotations to indicate that, which I can then extract from ExtensionContext from the Junit5 beforeAll methods
Further, I have a UserCredential class being and multiple #Bean definitions for that class for each type of user.
Code
Bean definition, using custom qualifier annotation User1Qualifier
#Bean
#User1Qualifier
public static UserCredentials clientBankLogin(
#Value(LOGIN_USERNAME_1) String username,
#Value(LOGIN_PASSWORD_1) String password) {
return new UserCredentials(username, password);
}
With my Custom qualifier being as below (there are multiple)
#Qualifier
#CustomValueAnnotation("User1")
#Retention(RUNTIME)
#Target({FIELD, PARAMETER, TYPE, METHOD})
public #interface User1Qualifier {}
Now, in the tests, I'm trying to use the same Annotation, which the ResetPortal picks up
#SpringBootTest
#ExtendWith({ResetPortal.class, SpringExtension.class})
final class LoginTest extends SpringTestBase {
#Test
#User1Qualifier
void clientLogin() {}
}
The ResetPortal class, since Junit5 initialises the class and calls it's managed instance, needs Autowired elements to be defined separately
#Service
public class ResetPortal implements BeforeEachCallback, AfterEachCallback {
static UserCredentials user1;
static UserCredentials user2;
Autowired
public void initializeLoginToPortal(#User1Qualifier UserCredentials u1,
#User2Qualifier UserCredentials u2) {
user1 = u1;
user2 = u2;
}
#Override
public void beforeEach(ExtensionContext context) {
// This is the custom Value annotation marked on each UserQualifier
// This is using AnnotationUtils#findAnnotation(java.lang.Class<?>, java.lang.Class<A>)
CustomValueAnnotation loginAs = getLoginAsAnnotation(context);
switch (loginAs){
case "User1" : loginWith(ResetPortal.user1); break;
case "User2" : loginWith(ResetPortal.user2); break;
}
}
}
I have had to adapt a project I've been working on to work differently, using an injected object (documentDao) to access the methods for adding/updating/etc. records in a database. Where necessary I simply injected this object into the constructor, but of course this won't work with JUnit tests (which can only have no-argument constructors), so I'm stuck on how to get the object into the test class.
The first code snippet shows a dumbed-down version of one of the test classes. The problem is that I need to create the documentDao object so I can pass it as an argument into the BackendApiController instantiation statement.
The second snippet is the first part of the DocumentDaoImpl class, which needs to be injected.
Any suggestions would be welcomed.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(loader = AnnotationConfigContextLoader.class)
public class ApiBackendTests {
#Configuration
#PropertySource(value = "classpath:system.properties")
static class ContextConfiguration {
}
private static BackendApiController backendApiController = new BackendApiController(documentDao);
#Test
public void retrieveSampleStatementList() {
String response = backendApiController.genericStatementList(x,y,z);
String eStatementId = "";
if (response.indexOf("_id") > 0) {
eStatementId = response.substring(response.indexOf("<_id>") + 5, response.indexOf("</_id>"));
}
// if this test is true, then at least one statement document was found in the above search.
assertTrue(response.indexOf("_id") > 0);
}
}
#Repository
public class DocumentDaoImpl<T> implements DocumentDao<T> {
public DocumentDaoImpl() {
}
#Inject
DBCollection dbCollection;
#Inject
GridFS gridFS;
#Autowired
ObjectMapper objectMapper;
#Override
public String insert(CommonDocument document) {
There's still not enough information to say anything for sure, but I believe you can try using #Autowired to wire up your needed component:
#Autowired
private DocumentDao documentDao;
You got the error:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'com.roler.res.test.ApiBackendTests': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.roler.res.mongodb.dao.DocumentDao
That means Spring isn't aware of the DocumentDao bean yet. There are several ways to do that, but I think the easiest way is putting this in your configuration context:
<context:component-scan base-package="package.contain.your.dao"/>
It will tell Spring to scan the package in search for components with annotation.
UPDATE: since you don't use XML configuration, #ComponentScan is the way to go
I have a production class which looks like
#Configurable
public class MyProductionClass {
#Resource private MyResource resource;
private String param;
public MyProductionClass(String param) {
this.param = param
}
public void aMethod() {
resource.doSomething();
//whatever, the previous line throws NullPointerException when testing
}
}
and a test class like
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = {"classpath:/test-context.xml"})
#DirtiesContext(classMode = ClassMode.AFTER_CLASS)
#TestExecutionListeners(listeners = {DependencyInjectionTestExecutionListener.class})
public class MyProductionClassTest {
private MyProductionClass underTest;
#Test
public void aMethodTest() {
underTest = new MyProductionClass("aString");
underTest.aMethod();
}
}
I have some log and I can see Spring context being initialized correctly, with all beans described in test-context.xml happily created, including resource.
But, when the test runs underTest.aMethod() a NullPointerException is thrown saying resource is null.
I use the same initialization (new MyProductionClass("aString")) in production and there everything works flawlessly. Is there something I am missing when using #Configurable classes with jUnit?
Versions are:
Spring 3.2.4
jUnit 4.11
EDIT My understanding of #Configurable: this issue may come from a misunderstanding of this feature. I understood it works so that I am not force to statically declare my #Configurable class in any Spring context (either Java or XML based), but so that I can initialized the class with the new operator and magically dependency are injected in the class. However, I do not know if this can work for tests (and I do not find any reason why it should not, a new is a new, whether it comes from a test class or from a production one)
you must get the bean from spring container
#Autowired
private MyProductionClass underTest;
...
#Test
public void aMethodTest() {
underTest.aMethod();
}
I'm trying to implement fine grained #Autowired configuration using basically the example from the spring documentation at: http://docs.spring.io/spring/docs/3.2.0.RELEASE/spring-framework-reference/html/beans.html#beans-autowired-annotation-qualifiers.
Given the following testcase:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes=ExampleConfiguration.class)
public class ExampleTest {
#Autowired #ExampleQualifier(key="x")
private ExampleBean beanWithQualifierKeyX;
#Test
public void test() {
System.out.println(this.beanWithQualifierKeyX);
}
}
and the following configuration:
#Configuration
public class ExampleConfiguration {
#Bean
#ExampleQualifier(key = "x")
public ExampleBean exampleBean1() {
return new ExampleBean();
}
#Bean
#ExampleQualifier(key = "y")
public ExampleBean exampleBean2() {
return new ExampleBean();
}
#Bean
public ExampleBean exampleBean3() {
return new ExampleBean();
}
}
with the custom qualifier annoation:
#Qualifier
#Retention(RetentionPolicy.RUNTIME)
public #interface ExampleQualifier {
String key();
}
What I would expect is the following: The property beanWithQualifierKeyX should be autowired using the first bean from the configuration class. Both the annotation on the configuration and the annotation on the property have the key="x" setting so this should be the only match. As far as I can see this is almost the same as MovieQualifier annotation from the Spring example documentation.
However, when I execute the test I get the following error:
org.springframework.beans.factory.BeanCreationException:
Could not autowire field: private xxx.ExampleBean xxx.ExampleTest.beanWithQualifierKeyX;
nested exception is
org.springframework.beans.factory.NoSuchBeanDefinitionException:
No unique bean of type [xxx.ExampleBean] is defined:
expected single matching bean but found 2: [exampleBean1, exampleBean2]
It looks like Spring does perform a match against the annotation (since both exampleBean1 and exampleBean2 are annotated) but doesn't take into account the value for the key of the annotation - otherwise x would be a perfect match.
Did I miss something in the configuration process or why is there no match?
The Spring version I'm using is 3.2.0.RELEASE
There is/was an bug in Spring 3.2.0 Autowiring with #Qualifier and #Qualifier meta annotation fails in Spring 3.2 (fixed in 3.2.1)
Its description sound exactly like your problem.
So update to 3.2.1