Trying to inject an object into JUnit test - java

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

Related

Injecting spring beans in non managed objects

the below bean of Class I want to inject in other non-managed bean, but it is not working as expected
#Component
#Setter
#Getter
public class AbstractLayoutProperties {
#Value("${spring.application.name}")
private String appName;
#Autowired
S3Service s3Service;
#Autowired
S3Client s3Client;
}
Below is the class which is not managed by the spring, but I am using #Configurable
#Configurable(preConstruction = true, autowire = Autowire.BY_NAME)
#EnableSpringConfigured
public class OverlayServiceImpl
implements GenericVehicleOverlayService<T, R> {
public findOnlyActive(){
appName = layoutProperties.getAppName(); // throwint NullPointerException beacuse the object not injected properly
}
#Autowired
AbstractLayoutProperties layoutProperties;
}
One more point, findOnlyActive method I am not calling directly, I am calling this from another service, lets say
#Service
public class OtherService{
public void findActive(){
OverlayServiceImpl impl=new OverlayServiceImpl();
impl.findOnlyActive();
}
#Autowired
OtherRepository otherRepo;
}
Problem statrement:
when impl.findOnlyActive(); is executed, it should inject all the required beans inside OverlayServiceImpl. In the same class I have two beans which are autowired, it seems none of them injected, this is the reaosn that every time I am encountering Nullpointer exception. so my question is how do I make it work, what are the steps and action I need to take so that spring will inject the dependencies in non managed object i,e OverlayServiceImpl.

Error org.springframework.beans.factory.UnsatisfiedDependencyException

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
}
}

Spring inject Repository mixed with normal property in constructor

I have Service class and Repository interface (Spring Data). I have also one abstract class:
public abstract class TestingMethod {
public TestingMethod() {
timeSum = 0;
}
protected long timeSum;
}
And class that extends it:
#Component
public class LimitTestingMethod extends TestingMethod {
#Autowired
private GeoTestDataRepository geoTestDataRepository;
private final int limitSize;
public LimitTestingMethod(int limitSize) {
super();
this.limitSize = limitSize;
}
}
In my Service I want to create instance of LimitTestingMethod and set its argument limitSize.
Something like:
LimitTestingMethod ltm3 = new LimitTestingMethod(3);
LimitTestingMethod ltm10 = new LimitTestingMethod(10);
But I got error:
Description: Parameter 0 of constructor in
com.exence.postgiscma.testingMethod.LimitTestingMethod required a bean
of type 'int' that could not be found. Action: Consider defining a
bean of type 'int' in your configuration.
Is that possible to achieve something like I want?
All best!
//EDIT
As I can see in comments it's bad approach. So maybe someone will give me advise how to project this better?
Is this good solution to pass repo as argument in constructor (I guess that not, but I can't get the idea how to do this better)?
LimitTestingMethod ltm3 = new LimitTestingMethod(3, geoTestDataRepository);
Is there a good and elegant solution?
As you are creating instances outside the scope of Spring your current solution won't work. The error comes from the fact that you have annotated it with #Component, it will detect it at startup and tries to create a bean, and fails.
To solve this you can do 1 of 2 things.
Let Spring handle the creation of the beans by using the ApplicationContext as a factory, providing additional arguments and make the bean prototype scoped.
Let Spring handle the injection after you manually created the instance using the ApplicationContext.
Use ApplicationContext as a factory
First make your bean a prototype so that it will be constructed when needed.
#Component
#Scope(
ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class LimitTestingMethod extends TestingMethod { ... }
Now an instance won't be created during startup. In your service inject the ApplicationContext and use the getBean method to get your desired instance.
public class Service {
#Autowired
private ApplicationContext ctx;
public void yourMethod() {
LimitTestingMethod ltm3 = ctx.getBean(LimitTestingMethod.class, 3);
LimitTestingMethod ltm10 = ctx.getBean(LimitTestingMethod.class, 10);
}
}
This will let Spring create the instance using the value passed in for the constructor and do the autowiring.
Injection after creation
Another solution is to manually create the instances and after that let Spring handle the auto wiring. You will lose the AOP abilities with this and will get only auto wiring.
First remove the #Component annotation from your LimitTestingMethod so it won't get detected during startup.
public class LimitTestingMethod extends TestingMethod { ... }
Now in your service autowire the ApplicationContext and after creating your bean use that to inject the dependencies.
public class Service {
#Autowired
private ApplicationContext ctx;
public void yourMethod() {
LimitTestingMethod ltm3 = new LimitTestingMethod(3);
LimitTestingMethod ltm10 = new LimitTestingMethod(10);
ctx.getAutowireCapableBeanFactory().autowireBean(lmt3);
ctx.getAutowireCapableBeanFactory().autowireBean(lmt10);
}
}
Both will achieve what you want, however, now your code directly depends on the Spring API. So instead of doing this, you are probably better of with another option and that is to inject everything for the LimitTestingMethod through the constructor and pass the repository yourself.
Use constructor to create an instance
public class LimitTestingMethod extends TestingMethod {
private final GeoTestDataRepository geoTestDataRepository;
private final int limitSize;
public LimitTestingMethod(int limitSize, GeoTestDataRepository geoTestDataRepository) {
this.limitSize=limitSize;
this.geoTestDataRepository=geoTestDataRepository;
}
}
Then you can simply autowire the repository in your service class and create the instances as needed (or create a factory which contains the complexity of creating this object).
public class Service {
#Autowired
private GeoTestDataRepository repo;
public void yourMethod() {
LimitTestingMethod ltm3 = new LimitTestingMethod(3, repo);
LimitTestingMethod ltm10 = new LimitTestingMethod(10, repo);
}
}

Provide external Bean before context start

I am developing a Java API for a service and I want to extract it to a library.
I am using spring 4.3.3
Right now there is a bean called ApiConfig which is simple pojo.
public class ApiConfig {
private String host;
private String username;
private String password;
}
and the values are read from a properties file.
I would like to be able to construct and provide this class before the context starts (several components have this class as #Autowired dependency).
For instance:
public class LoginService {
#Autowired
private ApiConfig apiConfig
[...]
}
Basically, I would like to do something like this:
public static MyApi get(ApiConfig apiConfig) {
//Here I want to provide this apiConfig as singleton bean that would be used everywhere
provide somehow this class as bean
// here all beans are loaded and the it fails because it cannot resolve ApiConfig
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ContextConfig.class);
MyApi myApi= context.getBean(MyApi.class);
return myApi;
}
The method MyApi.get(AppConfig) would be used by other java applications by adding dependency in pom.xml
Is there a way I can do this? Providing the ApiConfig bean and then initialize all the application?
Basically to let Spring know that there is also this bean, before starting context with new AnnotationConfigApplicationContext(ContextConfig.class)
UPDATE
The idea would be this, in any application using this library.
public static void main(String asdas[]) {
ApiConfig config = new ApiConfig();
config.setUsername("BOBTHEUSER");
//config.set etc
MyApi api = MyApi.get(config);
api.doOperation();
Actually #Autowire is enough. Make the ApiConfig a Bean and autowire it where it's is necessary. Spring resolves the proper order.
If you have two beans and one need the second to be initialized before creation use #DependsOn annotation
#Configuration
public class MainConfig {
#Autowired
private ApiConfig apiConfig
#Bean(name="apiConfig")
public ApiConfig apiConfig(){
... init the config ...
return apiConfigInstance;
}
#Bean(name="myApi")
#DependsOn("apiConfig")
public MyApi myApi(){
MyApi api = new MyApi(apiConfig);
return api;
}
}
Code from the example modified

Error creating bean because it's an interface?

Basically I have two beans implementing the same interface. One is for profile "default" and another "integration".
public interface SomeClientIfc { ... }
#Component
#Profile(value={"functional", "integration"})
public class StubSomeNIOClient implements SomeClientIfc {...}
public class SomeNIOClient implements SomeClientIfc {...}
#Configuration
#Profile("default")
public class SomeClientConfiguration {
#Bean
public SomeClientIfc someClient() {
...
SomeNIOClient someClient = new SomeNIOClient(numberOfParititions, controllerHosts, maxBufferReadSize,
connectionPoolSize);
return someClient;
}
}
In prod code it's
#Autowired
public SomeUserResolver(..., SomeClientIfc someClient) {...}
So far so good and I did see the stub bean is called in an integration test. Then I want to inject some test data into the stub bean in my integration test:
#ContextConfiguration(locations = {"/configProperties.xml", "/integrationTests.xml", ...})
#ActiveProfiles("integration")
public class SomeTestBase {
#Autowired
private SomeClientIfc someClientIfc;
}
However, when running the test I got error message
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'someClientIfc': Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.audiencescience.some.client.SomeClientIfc]: Specified class is an interface
I even tried to replace SomeClientIfc with StubSomeNIOClient but still get the same message, even though StubSomeNIOClient is not an interface.
You should add a Qualifier annotation along with the Autowired one to specify which bean must be instantiated:
#Autowired
#Qualifier("my-bean")
The reason it's trying to inject the SomeClientIfc is because you called the variable 'someClientIfc'.
In the integration environment you have all 3 classes initialized: SomeClientIfc, StubSomeNIOClient, and SomeNIOClient. This creates a confusion for spring, luckily there are ways to resolve that confusion.
One way is as mentioned above by Little Santi, another way is to name your variable 'stubSomeNIOClient' see code below
#ContextConfiguration(locations = {"/configProperties.xml", "/integrationTests.xml", ...})
#ActiveProfiles("integration")
public class SomeTestBase {
#Autowired
private SomeClientIfc stubSomeNIOClient;
}

Categories