#Value coming as null in junit 5 - java

I am testing my application but while running the test cases I am getting NUllPointer Exception as it's not able to map the value from YML file.
Can you please let me know how to achieve this ?
ControllerClass
class ControllerClass {
#Value("${app.items}")
String[] items; -- coming as null while running test cases
// remaing code
}
application-test.yml
app:
items: a, b, c, d
Test class
#SpringJUnitConfig
#SpringBootTest
#ActiveProfiles("test)
class TestControllerClass {
#InjectMock
ControllerClass controller;
#Mock
ServiceClass service;
#Test
//test case
}

Mockito doesn't know what to do - you can do it manually though:
#Before
public void setUp() {
String[] items = new String[2];
items[0] = "a";
items[1] = "b";
ReflectionTestUtils.setField(controller, "items",
items);
}

Naturally, we'll need a properties file to define the values we want to inject with the #Value annotation. And so, we'll first need to define a #PropertySource in our configuration class — with the properties file name.
#PropertySource("classpath:values.properties")
class ControllerClass {
#Value("${app.items}")
String[] items; -- coming as null while running test cases
// remaing code
}
if it is not working use . properties file like mentioned here.

Related

Junit Test case for applicationContext.getBean(class) returning null

When trying to write test case for applicationContext.getBean(classname). Getting null pointer exception.
Below is the Java class
#Service
#Slf4j
public class MyClassName {
#Autowired
ServiceOne serviceOne;
#Autowired
ApplicationContext applicationContext;
public getCitizenData(String mobileNumber) {
CitizenDataService citizenDataService = applicationContext.getBean(CitizenDataService.class, mobileNumber);
log.info("Getting Data");
return citizenDataService.searchMethod(mobileNumber)
// While debugging test file citizenDataService is coming as Null Hence getting Null Pointer exception
.flatMap(.............
)
Test File
class MyClassNameTest {
private MyClassName mockMyClassName;
#BeforeEach
void setUp() {
mockMyClassName = new MyClassName();
mockMyClassName.serviceOne = mock(ServiceOne.class);
mockMyClassName.applicationContext = mock(ApplicationContext.class);
//mockMyClassName.applicationContext.getAutowireCapableBeanFactory();
}
#Test
void testGetCitizenData() {
// Setup
// Configure ApplicationContext.getBean(...).
final CitizenDataService citizenDataService = new CitizenDataService("mobileNumber");
when(mockMyClassName.applicationContext.getBean(CitizenDataService.class, "args"))
.thenReturn(citizenDataService);
final result = mockMyClassName.getCitizenData("mobileNumber");
// While debugging this citizenDataService is coming as Null Hence getting Null Pointer exception
How to write test case for this ?
It is because you stub the incorrect arguments on the getBean() on the mocked ApplicationContext . When a method on a mock is called but there are no matching stubbed arguments , it will return either null or an empty collection , or 0 for an int/Integer and false for a boolean/Boolean by default . So in you case NULL CitizenDataService is returned.
Changing the following should fix your problem :
when(mockMyClassName.applicationContext.getBean(CitizenDataService.class, "mobileNumber"))
.thenReturn(citizenDataService);
Another way is not to mock the ApplicationContext but use spring test to really start up the spring container which include the beans there are required for the test case :
#ExtendWith(SpringExtension.class)
#ContextConfiguration(classes = {MyClassName.class,ServiceOne.class,CitizenDataService.class})
public class MyClassNameTest {
#Autowired
private MyClassName myClassName
#Test
void testGetCitizenData() {
}
}
This will be slower than the plain Mockito approach because it needs more time to start up the spring container and also require you to manage what beans to be included in the context.
For me , I would refactor your class such that it does not require to autowire ApplicationContext into it and then write a plain Mockito test. It is not a good practise to get the bean from the ApplicationContext manually which will make your class coupled to the spring framework codes.
I don't recommend mocking the application context. There are simply too many methods with similar arguments that it is difficult to mock the correct ones. Instead, use one of the readily available implementations written specifically for the use in tests, such as StaticApplicationContext.
class MyClassNameTest {
private MyClassName mockMyClassName;
private ApplicationContext appCtx;
#BeforeEach
void setUp() {
mockMyClassName = new MyClassName();
mockMyClassName.serviceOne = mock(ServiceOne.class);
this.appCtx = new StaticApplicationContext();
}
#Test
void testGetCitizenData() {
appctx.registerBean(CitizenDataService.class, () -> new CitizenDataService("mobileNumber"));
final result = mockMyClassName.getCitizenData("mobileNumber");

Java Unittest Mocked Unittest Not working

I'm trying to create a unittest for the method below (myHostClient), but I'm having some problems with it:
MyClass.java
import com.abc.def.ServiceBuilder
public class MyClass {
#Value("${materialLocation}")
private String materialValue
private static final SERVICEBUILDER = new ServiceBuilder()
#Bean public MyHostServiceClient myHostClient(
#Value(${qualifier_one}) final String qualiferOne,
#Value(${use_service}) final boolean useService) {
if(useService) {
return SERVICEBUILDER
.remote(MyHostServiceClient.class)
.withConfig(qualifierOne)
.withCall(new CallerAttach(Caller.retro(defaultStrategy())), // Error Line2 Here
new SigningVisitor(new CredentialsProvider(materialValue))),
call -> call.doSomeStuff(StuffObject.getStuffInstance()))
.makeClient();
}
#Bean DefaultStrategy<Object> defaultStrategy() {
final int valueA = 1;
final int valueB = 2;
return new DoSomeThingsBuilder()
.retry(valueA)
.doSomethingElse(valueB)
.create();
}
}
And here is my latest unsuccessful attempt at writing a unittest for it:
MyClassTest.java
import org.mockito.Mock
import static org.mockito.Mockito.times
public class MyClassTest {
#Mock
private SERVICEBUILDER serviceBuilder;
private MyClass myClass;
private String qualifierOne = "pass"
#BeforeEach
void setUp() {
myClass = new MyClass();
}
#Test
public void test_myHostClient() {
boolean useService = true;
final MyHostServiceClient result = myclass.myHostClient(qualifierOne, useService); // Error Line1 here
verify(serviceBuilder, times(1));
}
}
I have been trying to mock SERVICEBUILDER and verify that the mocked object is called one time but no luck so far. Right now I'm getting this error:
IllegalArgumentException: Material Name cannot be null
And it points to these lines in my code.
In the Test:
final MyHostServiceClient result = myclass.myHostClient(qualifierOne, useService);
Which points to this line in the module:
.withCall(new CallerAttach(Caller.retro(defaultStrategy())),
Anyone know how I can fix my unittest or write a working one from scratch?
I would say the design of MyClass is quite wrong because it looks like a Spring configuration but apparently it's not. If it is really supposed to be a configuration then I wouldn't even test it like this because it would rather be an integration test. Of course, even in integration tests you can mock dependencies. But the test itself would run differently and you would have to spin up a suitable Spring context, etc.
So given the above, I would rather make MyClass some sort of MyHostServiceClientFactory with removing all of the Spring annotations and then fix the following problems in your code.
SERVICEBUILDER is hardcoded.
SERVICEBUILDER is static final and its value is hardcoded into MyClass. You will not be able to reassign that field with the mocked version. It can still be final but not static then and it's better to use dependency injection here by passing the value through the MyClass constructor.
SERVICEBUILDER will still be not mocked even if you fix the above.
To really mock SERVICEBUILDER by using the #Mock annotation in the test you should enable Mockito annotations.
If you are using JUnit5 then you should annotate your test class like this:
#ExtendWith(MockitoExtension.class)
public class MyClassTest {
...
}
If you are stuck with JUnit4 then you should use another combination:
#RunWith(MockitoJUnitRunner.class)
public class MyClassTest {
...
}
Once you've done that the SERVICEBUILDER will be mocked but now you will have to configure the behaviour of that mock, like what is going to be returned by the SERVICEBUILDER methods. I can see 4 methods in total, namely remote, withConfig, withCall, and makeClient. You will have to do Mockito's when/thenReturn configurations.
MyClass.materialValue is null.
But even when your mock will be properly configured you will still encounter the original IllegalArgumentException: Material Name cannot be null. This is because MyClass.materialValue will still be null and looks like CredentialsProvider cannot accept that. As I can see, that field is supposed to be injected by Spring using the #Value annotation, but remember this class no longer contains anything from Spring. As in problem 1, you have to pass the value through the MyClass constructor.
Once all of these problems are solved you can introduce a thin Spring configuration like MyHostServiceClientConfiguration (or whatever name suits you) that would serve as a provider of necessary properties/dependencies for MyHostServiceClientFactory (existing MyClass) and then this factory can provide you with a MyHostServiceClient bean through a method like MyHostServiceClientConfiguration#myHostServiceClient annotated with #Bean.
Conceptually your MyHostServiceClientFactory will look like this:
public class MyHostServiceClientFactory {
private final String materialValue;
private final ServiceBuilder serviceBuilder;
public MyHostServiceClientFactory(String materialValue, ServiceBuilder serviceBuilder) {
this.materialValue = materialValue;
this.serviceBuilder = serviceBuilder;
}
public MyHostServiceClient myHostClient(String qualiferOne, boolean useService) {
if(useService) {
return serviceBuilder
.remote(MyHostServiceClient.class)
.withConfig(qualifierOne)
.withCall(new CallerAttach(Caller.retro(defaultStrategy())), // Error Line2 Here
new SigningVisitor(new CredentialsProvider(materialValue))),
call -> call.doSomeStuff(StuffObject.getStuffInstance()))
.makeClient();
}
// can also be injected as a dependency rather than being hardcoded
DefaultStrategy<Object> defaultStrategy() {
final int valueA = 1;
final int valueB = 2;
return new DoSomeThingsBuilder()
.retry(valueA)
.doSomethingElse(valueB)
.create();
}
}

Is there a way of executing consecutive n number integration-tests over 1 instances of dockercompose (test-containers)

Using DockerComposeContainer from test-containers api in SpringBootApp I have the following issue. The DockerComposeContainer object from test-containers must be annotated with #ClassRule to be started and closed automatically thats give the need the field to be static because of the #ClassRule annotation. If I execute single test it finish successfully but when I execute more than 1 consecutive test's I get error on the second test
Could not start container
org.testcontainers.containers.ContainerLaunchException: Aborting attempt to link to container cn6j5atqrtav_janusgraph_1 as it is not running
at org.testcontainers.containers.GenericContainer..
it come's becouse I use static field for DockerComposeContainer. If i use just #Rule then I cannot pass DockerComposeContainer instance to the #TestConfiguration inner static class's bean that use this field. Why I should use the inner static class, because I override some bean's from the SpringBoot Application's Context. Here is some little example:
#RunWith(SpringRunner.class)
public class IntegrationTestBase {
private static final String DOCKER_COMPOSE_FILE = "src/test/resources/integration/docker-compose-cql-es.yml";
#ClassRule
public static DockerComposeContainer environment =
new DockerComposeContainer(new File(DOCKER_COMPOSE_FILE))
.withExposedService("janusgraph", 8182, Wait.forListeningPort()
.withStartupTimeout(Duration.ofMinutes(1)));
#TestConfiguration
public static class ContextConfig {
#Bean(Constants.GRAPH_TRAVERSAL_SOURCE)
#Scope("prototype")
public GraphTraversalSource graphTraversalSource() {
org.apache.commons.configuration.Configuration remoteConfig = new BaseConfiguration();
remoteConfig.addProperty("gremlin.remote.remoteConnectionClass", "org.apache.tinkerpop.gremlin.driver.remote.DriverRemoteConnection");
remoteConfig.addProperty("gremlin.remote.driver.sourceName", "g");
remoteConfig.addProperty("gremlin.remote.driver.clusterFile", "remote-objects.yaml");
try {
org.apache.commons.configuration.Configuration clusterConfig = new PropertiesConfiguration();
clusterConfig.addProperty("hosts", new String[]{environment.getServiceHost("janusgraph", 8182)}); // I want to use environemnt here without beign static ?
clusterConfig.addProperty("port", environment.getServicePort("janusgraph", 8182));
clusterConfig.addProperty("serializer.className", GryoMessageSerializerV3d0.class.getName());
clusterConfig.addProperty("serializer.config.ioRegistries", JanusGraphIoRegistry.class.getName());
clusterConfig.addProperty("serializer.config.ioRegistries", JanusGraphIoRegistry.class.getName());
final Cluster cluster = Cluster.open(clusterConfig);
Constructor<DriverRemoteConnection> constructor = DriverRemoteConnection.class.getDeclaredConstructor(Cluster.class, boolean.class, String.class);
constructor.setAccessible(true);
DriverRemoteConnection connection = constructor.newInstance(cluster, true, "g");
return JanusGraphFactory.open("inmemory").traversal().withRemote(connection);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
}
One thing I have tried is to remove the static from DockerComposeContainer and to use #Rule instead of #ClassRule and to make the inner class non static but then I don't override the current SpringBoot Environment and I need to use #Import the IntegrationTestBase into the Inner class to #AutoWired the DockerComposeContainer and I got circular dependency issue that way. Sorry if the question got too broad. That class is a base class to be extended by the test classes, because of that it doesn't have #Test inside it. Link that describe the Configuration of SpringBoot Test's https://howtodoinjava.com/spring-boot2/testing/springboot-test-configuration/
The DockerComposeContainer object from test-containers must be annotated with #ClassRule to be started and closed automatically
There is no MUST, rather SHOULD.
You can drop the annotation altogether and use the Singleton Container pattern, for example.

I can get a Spring #Value at one of my test classes, but can't at other

Im testing with JUnit a project, and it will take some values from an application-test.properties.
I made a class called TestPropertiesProvider which is meant to get the values and send them to my test classes but is not working while at my test class i can get the values with no problem!
This is my test class. (Here my Value annotation is working and im getting my props).
#SpringBootTest
#TestPropertySource(locations = "classpath:application-test.properties")
#TestInstance(Lifecycle.PER_CLASS)
// The annotations SpringBootTest, and TestPropertySource, shouldn't be here, just put them to see if i could
// get the values here.
public class ODServiceImplTest {
#Value("${ondemand.server.port}")String si; // THIS IS WORKING, BUT I DONT WANNA USE THE #Value HERE!
ODService odService = new ODServiceImpl();
TestPropertiesProvider propsProvider = new TestPropertiesProvider();
#BeforeEach
void setUp() {
ReflectionTestUtils.setField(odService, "converterFactory", new ConverterFactory());
ReflectionTestUtils.setField(odService, "SERVER_NAME", propsProvider.getSERVER_NAME());
ReflectionTestUtils.setField(odService, "SERVER_PORT", propsProvider.getSERVER_PORT());
ReflectionTestUtils.setField(odService, "USER_ID", propsProvider.getUSER_ID());
ReflectionTestUtils.setField(odService, "PASSWORD", propsProvider.getPASSWORD());
}
#Test
void getDocumentDtoByValueTest() {
...
...
And this is my TestPropertiesProvider class, i want my values to be taken here, so with the getters i can use them here.
#SpringBootTest
#TestPropertySource(locations = "classpath:application-test.properties")
#Component
public class TestPropertiesProvider {
// Connection params.
#Value("${ondemand.server.port}")String si; // THIS IS NOT WORKING, I WANT THIS TO WORK.
private final String USER_ID = "DH01A";
private final String PASSWORD = "AR2SB";
private final String SERVER_NAME = "servername...";
private final String SERVER_PORT = "1415";
// Example values for OnDemand searchs.
private final String CORRECT_FOLDER_NAME = "DECF001";
private final String CORRECT_FOLDER_NAME_2 = "DH0001";
private final String NRO_ENVIO_SEARCH_VALUE = "20180500022E";
private final String NRO_ENVIO_SEARCH_FIELD = "NRO_ENVIO";
private final String PDF_FORMAT = "PDF";
// Utility methods and getters...
...
...
Hope anybody can help me, thanks! I'm using Spring Boot and JUnit 5.
UPDATE: The not working #Value 's are setting null at the variables.
My both java classes are at the same application, at 'src/test/java/' but at differents packages. My application have only one 'application-test.properties' at my 'src/test/resources/ folder.
Finally i could solve it, adding the annotation #TestInstance(Lifecycle.PER_CLASS).
This way:
#SpringBootTest
#TestPropertySource(locations = "classpath:application-test.properties")
#TestInstance(Lifecycle.PER_CLASS)
#Component
public class TestPropertiesProvider {
...
...
I really don't know how is this working, but solved me problem. I would really appreciate if somebody can explain it to me! Searching at Docs says that my instance will be just one of TestPropertiesProvider for all my tests, but that doesn't explain the problem I got.
In your ODServiceImplTest class your are creating an instance of TestPropertiesProvider class manually instead of using spring dependency injection mechanism.
So this will not inject the value of ondemand.server.port property in si variable. Instead you need to autowire TestPropertiesProvider in ODServiceImplTest as below.
#Autowired
TestPropertiesProvider propsProvider;

How can #Autowired object be initialized when in a class with non-default constructor? [duplicate]

This question already has answers here:
Why is my Spring #Autowired field null?
(21 answers)
Closed 3 years ago.
Language/frameworks : Java8/Spring 4.3.9/openjpa 2.4.3
Being a Spring newbie, I suspect I am not applying the proper pattern here.
The scenario is something like what is shown below. However, I cannot get objectB to initialize and a Null Pointer exception is thrown. I suspect that the problem is that Spring is unable to initialize class A but I cannot set the "#Component" annotation to class A as it does not have a default constructor. Is there a workaround for this scenario ? A pattern that should be followed perhaps ? This is legacy code that is being retrofitted with Spring.
#ContextConfiguration(locations = { "classpath:/context.xml" })
ClassTestA{
#Test
public void TestA(){
:
:
A a = new A ("A", "B", "C", "D");
:
:
// Following line, Throws a null pointer exception in the doSomething() method.
someobject.someMethod(a.doSomething());
}
}
class A {
#Autowired
private B objectB;
public A (string t, string m, string x, string y)
{
// variable inits
}
// C class represents a database table Entity
public C doSomething()
{
C c = new C();
c.someWork(objectB.method()); // throws a null pointer exception because obJectB is null !
}
}
#Component
public class B
{
// No constructor
}
In your test you have created object of class A by using new keyword. Once you do it, spring will not take care of dependencies in class A, so it won't autowire class B object. For spring to autowire the dependencies of class, the class itself should be manged by spring. Another way around will be to write a method annotated with #Bean, and inside that method you can create object of class A with any constructor of your choice with default values, this way you autowire class A as well in your test instead of using new keyword. You don't have to then annotate class A with #Component.
#Bean
public A getA() {
return new A(null,null,null,null);
}
You can write above code in a class annotated with #Configuration, so that spring will read and create of object of A and put it in container.
You can use both XML & annotation based configuration at same time. Just need to add a tag in sprint.xml (Please search tag on google)
In your test you can say
#Autowired
private A aObject

Categories