Java - Constructor Unit testing - java

I have following Java configuration class which I need to unit test using JUnit:
public class Config {
private static final String AMQ_CONNECTION_URL_TEMPLATE = "failover:(%s)";
private final String awsAmqUrl;
public Config(String url, Optional<String> amqConnectionOptions, PropertiesManager propertiesManager) {
String urlParameter = propertiesManager.getStringParameter(url);
this.awsAmqUrl = constructAmqConnectionString(urlParameter, amqConnectionOptions);
}
private String constructAmqConnectionString(String urlParameter, Optional<String> connectionOptions) {
if (connectionOptions.isPresent()) {
urlParameter = Stream.of(urlParameter.split(","))
.map(url -> url + "?" + connectionOptions.get())
.collect(Collectors.joining(","));
}
return String.format(AMQ_CONNECTION_URL_TEMPLATE, urlParameter);
}
public ConnectionFactory getConnectionFactory() {
ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory(awsAmqUrl);
return connectionFactory;
}
}
I am struggling to find an optimal solution for constructAmqConnectionString method unit testing as it's marked as private.
There are 3 scenarios I am trying to cover:
urlParameter - comprises comma separated URLs (url1,url2),
connectionOptions is not empty;
urlParameter - comprises comma
separated URLs (url1,url2), connectionOptions is empty;
urlParameter - comprises single URL (url1), connectionOptions is
not empty.
Current solution is to add a getter into Config class for awsAmqUrl field so that logic of constructor's call can be verified/tested:
public String getAwsAmqUrl() {
return this.awsAmqUrl;
}
Tests itself have following logic:
#Test
public void verifyConstructorWithoutMqOptionsMultiBroker() {
when(propertiesManager.getStringParameter(any())).thenReturn("url1,url2");
Optional<String> amqConnectionOptions = Optional.empty();
config = new Config("url1,url2", amqConnectionOptions, propertiesManager);
assertEquals(String.format("failover:(url1,url2)"),config.getAwsAmqUrl());
}
#Test
public void verifyConstructorWithMqOptionsMultiBroker() {
when(propertiesManager.getStringParameter(any())).thenReturn("url1,url2");
Optional<String> amqConnectionOptions = Optional.of("optionTest=1");
config = new Config("url1,url2", amqConnectionOptions, propertiesManager);
assertEquals(String.format("failover:(url1?%1$s,url2?%1$s)",amqConnectionOptions.get()),config.getAwsAmqUrl());
}
#Test
public void verifyConstructorWithMqOptionsSingleBroker() {
when(propertiesManager.getStringParameter(any())).thenReturn("url1");
Optional<String> amqConnectionOptions = Optional.of("optionTest=1");
config = new Config("url1", amqConnectionOptions, propertiesManager);
assertEquals(String.format("failover:(url1?%1$s)",amqConnectionOptions.get()),config.getAwsAmqUrl());
}
Adding a getter just for Unit testing purposes doesn't feel the right thing to do as it's breaking encapsulation.
Is there a better way to approach testing in such scenario?

The only place that your class uses awsAmqUrl is in the getConnectionFactory method. So it looks like this is the method you'll have to use, to make sure the value of awsAmqUrl is correct. So instead of having a getter for awsAmqUrl, use something like
String storedUrl = objectUnderTest.getConnectionFactory().getBrokerUrl();
and then you can make assertions on that URL.
Sure, it makes your test dependent on the behaviour of ActiveMQConnectionFactory - but that's OK, since your class is tightly coupled to that particular class anyway.

Related

How to write unit test for URI create in java

Below is the method I'm trying to write unit test using junit 5
#Value("${proxy.host}")
private String endpoint;
public Request<Void> setAwsRequestGETParameter(String setStatusPath) {
Request<Void> requestAws = new DefaultRequest<Void>("sts");
requestAws.setHttpMethod(HttpMethodName.GET);
requestAws.setEndpoint(URI.create(endpoint));
requestAws.setResourcePath(setStatusPath);
return requestAws;
}
Below is the unit test I'm trying to run
#InjectMocks
private AWSAuthHandler testAWSAuthHandler;
#Test
public void testSetAwsRequestGETParameter() throws Exception {
URI mockedURI = Mockito.mock(URI.class);
assertNotNull(testAWSAuthHandler.setAwsRequestGETParameter("/status/7deaed5e-3080-45ec-89ba-403977d60c0c"));
}
Below is the stack trace:
java.lang.NullPointerException
at java.base/java.net.URI$Parser.parse(URI.java:3106)
at java.base/java.net.URI.<init>(URI.java:600)
at java.base/java.net.URI.create(URI.java:881)
Can someone please help me with the missing part? Thank you
For setting properties of class that you can't mock you can use Spring Reflection Utils, like that:
ReflectionUtils.setField(field, target, value);
where the field is the name of the field which you want to set ("endpoint" for your case),
target is the mocked class (testAWSAuthHandler for your case)
value is the wanted value
As Sweta Sharma said, you need to initialise AWSAuthHandler with some value for endpoint field. That's why it is better to use constructor injection rather than field one.
Assuming your AWSAuthHandler class look like this (as you didn't provide the code for the whole class):
public class AWSAuthHandler {
#Value("${proxy.host}")
private String endpoint;
public Request<Void> setAwsRequestGETParameter(String setStatusPath) {
Request<Void> requestAws = new DefaultRequest<Void>("sts");
requestAws.setHttpMethod(HttpMethodName.GET);
requestAws.setEndpoint(URI.create(endpoint));
requestAws.setResourcePath(setStatusPath);
return requestAws;
}
You can refactor it in the following way:
public class AWSAuthHandler {
private String endpoint;
public AWSAuthHandler(#Value("${proxy.host}") String endpoint) {
this.endpoint = endpoint;
}
public Request<Void> setAwsRequestGETParameter(String setStatusPath) {
Request<Void> requestAws = new DefaultRequest<Void>("sts");
requestAws.setHttpMethod(HttpMethodName.GET);
requestAws.setEndpoint(URI.create(endpoint));
requestAws.setResourcePath(setStatusPath);
return requestAws;
}
Then you can create tests for this class:
private AWSAuthHandler testAWSAuthHandler;
#BeforeEach
void setUpTests() {
this.testAWSAuthHandler = new AWSAuthHandler("some-endpoint-here");
}
#Test
public void testSetAwsRequestGETParameter() throws Exception {
assertNotNull(testAWSAuthHandler.setAwsRequestGETParameter("/status/7deaed5e-3080-45ec-89ba-403977d60c0c"));
}
You can read more about Spring #Value annotation here, for example: https://www.baeldung.com/spring-value-annotation

Condition fails in Junit5 & Mockito

I am trying to write the test case for my application and I cannot get past a condition even after providing what is expected, from what I know.
Here is my test class.
#ExtendWith(MockitoExtension.class)
class AppConfigTest {
#Mock
#TempDir
File mockedFile;
#InjectMocks
private AppConfig appConfig;
#Test
void getData() throws Exception {
File f = new File("f");
File[] files = {f};
lenient().when(mockedFile.listFiles()).thenReturn(files);
lenient().when(mockedFile.isFile()).thenReturn(true);
assertNotNull(appConfig.getData());
}
}
My implementation. The test doesn't go past the if condition. The test does not cover the code after the condition as it turns true all the time. I need my test to cover keyMap() in the last line.
private Map<String, String> getData() {
File[] files = new File(APP_CONST.DIRECTORY).listFiles();
if (null == files) { // not turning FALSE even after providing mocked "files" array
return Collections.emptyMap();
}
List<String> keysList = getKeyList(files);
return keyMap(APP_CONST.DIRECTORY, keysList);
}
Can anyone please tell me how to correct this please? Using SpringBoot/JUnit 5
We discussed this in the comments, but in any case, I guess an example is better.
One way you could go about this is to make sure the same folder exists. In the test setup you could simply create it.
#Before
public void setUp() {
new File(APP_CONST.DIRECTORY).mkdirs();
}
Now when accessing it in the implementation there will be a directory. You can also inside the test add files to the directory, so it's not empty.
Although this works, it has some issues with setting it up and cleaning it up. A better way is to abstract this from the implementation itself and use some kind of provider for it.
A suggestion would be to create an interface where the real implementation returns the real folder and in tests you can mock this.
public interface DirectoryProvider {
public File someDirectory();
}
public class RealDirectoryProvider implements DirectoryProvider {
#Override
public File someDirectory() {
return new File(APP_CONST.DIRECTORY);
}
}
you can now make the getData class depend on this abstraction. You didn't give us the class name, so don't pay attention to that part:
public class Data {
private final DirectoryProvider directoryProvider;
public Data(DirectoryProvider directoryProvider) {
this.directoryProvider = directoryProvider;
}
private Map<String, String> getData() {
File[] files = directoryProvider.someDirectory().listFiles();
if (null == files) {
return Collections.emptyMap();
}
List<String> keysList = getKeyList(files);
return keyMap(APP_CONST.DIRECTORY, keysList);
}
}
Now during the test you can just inject your mocked directory/temp dir.
#ExtendWith(MockitoExtension.class)
class AppConfigTest {
#TempDir
File mockedFile;
#Mock
DirectoryProvider directoryProvider;
#InjectMocks
private AppConfig appConfig;
#Test
void getData() throws Exception {
lenient().when(directoryProvider.someDirectory()).thenReturn(mockedFile);
assertNotNull(appConfig.getData());
}
}
You can also add files to the temp dir if you need. This however should be enough to pass the if I think.

New to Java. Need help to understand testing for my function with some dependencies

#Autowired
private Publisher Publisher;
private int Id = 12345;
private BClient bClient = new BClient(Id);
private Map<Integer, Boolean> defaultBooleanValueMap;
private LoadCache<Integer, Boolean> booleanCache = CacheBuilder.newBuilder()
.refreshAfterWrite(refreshRate, TimeUnit.SECONDS)
.build(
new CacheLoader<Integer, Boolean>() {
#Override
public Boolean load(Integer id) throws Exception {
return fetchBooleanValue(id);
}
}
);
private boolean fetchBooleanValue(int id) {
long fetchStart = System.currentTimeMillis();
boolean val = bClient.getBoolean(id, defaultBooleanValueMap.get(id));
publisher.publish(
publisher.getDistributionMetric("fetchtime.bool", System.currentTimeMillis() - fetchStart));
return val;
}
public boolean getBoolean(int id) {
return booleanCache.getUnchecked(id);
}
//Trying to test getBoolean(int id) function. I'm mocking bClient, Publisher. Not sure how to properly test it
// Could anyone help me understand how to test it
//testing with
SomeClass someClass = new SomeClass();
#Mock
Publisher publisher;
#Mock
BClient bClient;
#Test
public void testGetBoolean(){
bClient = new BClient(12345);
Map<Integer,Boolean> defaultBooleanValueMap = null;
defaultBooleanValueMap.put(123, false);
when(bClient.getBoolean(123,
defaultBooleanBregValueMap.get(123))).thenReturn(false);
boolean b = someClass.getBoolean(123);
assertFalse(b);
}
// i'm don't know if i'm doing it right
Are you using Mockito?
It's good practice to not start a field name with a capital (Publisher for instance)
Personally i think it will be better to make all these methods protected instead of private, so that you can test each of them separately.
however this would be an example of a unit test for your code.
You can use Mockito to check if certain method calls are fired the amount of time you expect them to be fired.
I did not include all but you can just add if you need more tests.
Further i recommend to read about Mockito as it has some really powerful unit test tools
#Test
public void testGetBoolean () {
xxx.getBoolean
//the following line can only be done if you spy your service
Mockito.verify(xxx, times(1)).fetchBooleanValue(any());
//this line can be done if you mock bClient
Mockito.verify(bClient , times(1)).getBoolean(any(), any()); //Mockito.any() or you can fill in the real values if you really want.
//this line can be done if you mock Publisher
Mockito.verify(publisher, times(1)).publish(any); //again any or the real value you want to pass
}
I just now saw your unit tests, you can inject the mocks in you class with the following anotatation:
#InjectMocks
SomeClass someClass;
when mocking a class you don't manually have to create it again.
You don't have to mock the Bclient as you already create it with "new Bclient" instead of autowiring it.
I feel the #InjectMocks is not working because you didn't tell Spring that your class is a service component.
#Service
public class SomeClass {
//insert code here
}

Set system property in jmockit unit test

Using a combination of testng and jmockit to do some unit testing. In a method I am testing, it tries to access a system property that I set using a JBoss deploy script, therefore my unit test doesn't have access to the entire JBoss environment to access the properties, so it returns null when testing that method. Tried mocking and setting the system variable directly in my test, but the system property is still returning null in the class that I am testing.
The class being tested:
//returns the correct value in the application but returns null for the test
public static final String webdavrootDir = System.getProperty("property.name");
public String getFileUrl(){
StringBuilder sb = new StringBuilder();
return sb.append(webdavrootDir)
.append(intervalDir)
.append(fileName)
.toString();
}
The test:
#Test
public void getUrl(#Mocked System system){
system.setProperty("property.name", "https://justatest.com/dav/bulk");
String fileUrl = csvConfig.getFileUrl();
assertEquals(fileUrl, "https://justatest.com/dav/bulk/otherstuff");
}
The test finds the value null/otherstuff but expects https://justatest.com/dav/bulk/otherstuff
I have also tried setting the system property in a testng #BeforeMethod method without any success.
Make sure you call System.setProperty("property.name", "https://justatest.com/dav/bulk"); before the class being tested is instantiated, otherwise the static field will always be null.
Consider using a #BeforeClass setup method for this:
#BeforeClass
public static void setup() {
System.setProperty("property.name", "https://justatest.com/dav/bulk");
// Instantiate your CsvConfig instance here if applicable.
}
and then
#Test
public void getUrl(){
System.setProperty("property.name", "https://justatest.com/dav/bulk");
String fileUrl = csvConfig.getFileUrl();
assertEquals(fileUrl, "https://justatest.com/dav/bulk/otherstuff");
}
If you want to use jMocki, but you have to make sure it is done, before your class-under-test is beeing loaded (because of the static field).
#BeforeClass
public static void fakeSystemProperty() {
new MockUp<System>() {
#Mock
public String getProperty(String key) {
return "https://justatest.com/dav/bulk";
}
};
}
Another way would be to modify the class-under-test and partially mock this one, e.g.:
public class CsvConfig {
private static final String webdavrootDir = System.getProperty("property.name");
public static String getProperty() {
return webdavrootDir;
}
}
The test:
#BeforeClass
public static void fakeSystemProperty() {
new MockUp<CsvConfig>() {
#Mock
public String getProperty() {
return "https://justatest.com/dav/bulk";
}
};
}

Make a static variable injectable

i am building a http API client that needs to call out to a specific endpoint like so:
public class MyApiClient {
private static final String ENDPOINT ="http://myapi....";
}
Here the endpoint won't change so its constant. However, I want to be able to override this for testing so that I can test against a mock http server for example.
Whats the best way to do this? Is it just to make it an instance variable and provide it with a starting value:
private String endpoint = ="http://myapi....";
public void setEndpoint(String endpoint){
...
}
Well, there are of course many solutions to this and one way of doing it is to use a system property with a default value:
private static final String DEFAULT_ENDPOINT = "http://myapi....";
private static final String ENDPOINT =
System.getProperty("my.endpoint", DEFAULT_ENDPOINT);
This way you get a configurable way of solving your problem. If you need even more flexibility when initializing your static constants you could also use a static initializer:
private static final String ENDPOINT;
static {
// do initialization here but do not throw any exceptions (bad practice)
// you can e.g. read from files etc...
// Then assign your constant...
ENDPOINT =
}
System properties are passed on the command line as -D parameters e.g:
java -Dmy.endpoint=http://...
But in my opinion, an even better approach is to actually inject the value to the class that is using it:
public class ClassThatIsUsingTheConfig {
private final String endpoint;
public ClassThatIsUsingTheConfig(final String endpoint) {
this.endpoint = endpoint;
}
public void someMethod() {
// use endpoint
}
}
And then, make the selection of which endpoint to use in the caller class. From a test case, this will be very easy to mock.
public class MyTest {
#Test
public void testMethod() {
ClassThatIsUsingTheConfig var = new ClassThatIsUsingTheConfig(TEST_ENDPOINT);
var.someMethod();
}
}
public class MyProdClass {
public void prodMethod() {
ClassThatIsUsingTheConfig var = new ClassThatIsUsingTheConfig(PROD_ENDPOINT);
var.someMethod();
}
}
You can read more about dependency injection here.
On a side note, if you are using some kind of framework for managing dependencies such as Spring Framework or CDI it is common to be able to inject properties and constants in various ways (e.g. based on which environment that is currently running). An example, when using Spring Framework you can declare all your constants in a property file and inject the property using annotations:
#Autowired
public ClassWhoIsUsingTheConfig(#Value("my.endoint") final String endpoint) {
this.endpoint = endpoint;
}
The property file for prod could be along the lines of:
my.endpoint=http://prodserver...
wheras the property file for test would look like this:
my.endpoint=http://testserver...
The approach of using a Dependency Injection engine allows for a very flexible way of handling external constants, paths, resources etc and simplifies your life when it comes to testing the code.

Categories