How to unit test methods with dependencies on other methods - java

I have the following method
public static File getInventoryFileFromProperties(){
String filePath = getProperty(ConfigProperties.MY_INVENTORY_FILE);
logger.debug("Looking for inventory file at {}", filePath);
return new File(filePath);
}
How do i unit test this for the following condition, ConfigProperties.MY_INVENTORY_FILE is not present in the properties file.
getProperty() // gets values from property file
ConfigProperties.MY_INVENTORY_FILE // is an enum of keys

The best way to make code that accesses external resources -- such as the file system -- unit testable, is by creating an abstraction layer, e.g.:
public class FileAccessor {
public String getProperty(ConfigProperties property) {
// property file access code goes here
}
public File createFile(String filePath) {
return new File(filePath);
}
}
Then, the class-under-test can be refactored to use the resource-accessor through constructor injection of the dependency:
public class ContainingClass {
private FileAccessor fileAccessor;
// this constructor is accessible for the unit tests
ContainingClass(FileAccessor fileAccessor) {
this.fileAccessor = fileAccessor;
}
// this constructor is used by normal client code
public ContainingClass() {
this(new FileAccessor());
}
public File getInventoryFileFromProperties(){
String filePath = fileAccessor.getProperty(ConfigProperties.MY_INVENTORY_FILE);
return fileAccessor.createFile(filePath);
}
}
Finally, unit testing becomes simpler now that you can mock the file access. This test uses the Mockito mocking framework for mocking the dependency, and also works with earlier versions of JUnit:
import static org.mockito.Mockito.*;
import org.junit.Test;
public class ContainingClassTest {
#Test
public void getInventoryFileFromProperties_MY_INVENTORY_FILE_isMissing() {
FileAccessor fileAccessor = mock(FileAccessor.class);
// arrange the config file to return a null value for the property
when(fileAccessor.getProperty(ConfigProperties.MY_INVENTORY_FILE)).thenReturn(null);
// act; call the method
new ContainingClass(fileAccessor).getInventoryFileFromProperties();
// assert that the file-creating method was called with a null path
verify(fileAccessor).createFile(isNull(String.class));
}
}

Related

Unit test file content creation before it being deleted

I have the following JAVA code:
#Override
public void myFunc(...) {
String content = createFileContent(...);
File f = createFile(content);
uploadFile(f);
deleteFile(f)
}
I want to test to file content, or the file itself (both can work), using unit test.
The file is deleted before the function ends.
What is the approach you would recommend on taking here? (I am using Mockito as mocking framework)
Spy the target test class and define the behavior and verify the calls of the methods, and if you want to assert the argument you can use ArgumentCaptor
public class TargetTest {
#Test
public void shouldDoSomething() {
// Arrange
final String content = "content-file";
final File file = new File();
Target target = spy(target);
doReturn(content).when(target).createFileContent();
doReturn(file).when(target).createFile(content);
doNothing().when(target).uploadFile(file);
doNothing().when(target).deleteFile(file);
// Act
target.myFunc();
// Assert
verify(target).createFileContent();
verify(target).createFile(content);
verify(target).uploadFile(file);
verify(target).deleteFile(file);
}
}

Unit test method does not use the mocked object when running the test. Instead calls the actual class and method

I have a class to test named ClassToTest. It calls a CloudService to upload file.
public class ClassToTest {
public String moveFilesToCloud(String path, String documentclass, String objStore) {
log.info("Moving files to cloud.");
String docId = StringUtils.EMPTY;
CloudService service = new CloudService();
try {
docId = service.uploadDocument(path,documentclass,objStore,"");
} catch (CloudException e) {
log.info("* Error uploading reports to cloud *" + e.getMessage());
}
return docId;
}
}
Below is the test class. The test class has a mocked object for CloudService. When I run the test instead of getting the mocked object, the actual CloudService is executed and fails.
#Mock
CloudService cloudService;
#InjectMocks
ClassToTest classToTest;
#Test
public void testMoveFilesToCloud() throws Exception {
String pass = "pass";
when(cloudService.uploadDocument("abc","def","ghi","")).thenReturn(pass);
String result = classToTest.moveFilesToCloud("abc","def","ghi");
assertEquals(result,pass);
}
I am expecting the mocked object for CloudService to be used when executing this line -
CloudService service = new CloudService();
Instead, it is actually trying to create a new instance of CloudService.
Where am I going wrong here?
Try to use dependency injection. Make CloudService a field of ClassToTest. Change the constructor of ClassToTest to accept a CloudService. Then Mockito is able to inject the mock into ClassToTest in your unit test.
public class ClassToTest {
private CloudService service;
public ClassToTest(CloudService service) {
this.service = service;
}
public String moveFilesToCloud(String path, String documentclass, String objStore) {
log.info("Moving files to cloud.");
String docId = StringUtils.EMPTY;
try {
docId = service.uploadDocument(path,documentclass,objStore,"");
} catch (CloudException e) {
log.info("* Error uploading reports to cloud *" + e.getMessage());
}
return docId;
}
}
This will not work.
If you had used injection, then adding #RunWith(MockitoJUnitRunner.class) would have been useful, but it is not.
If you can use Injection, then do it, otherwise you have to use PowerMockito in order to modify bytecode and produce a mock when invoking a constructor. This can help you

Mock private method with new object creation

I've written a class which reads the entire file and returns the content.
class ClassToTest {
public methodToTest(String input) {
return privateMethod(input);
}
private privateMethod(input) {
ClassPathResource classPathResource = new ClassPathResource(input);
IOUtils.toString(classPathResource.getFile());
}
}
Now, inside my test class, I don't want my test to actually read the file from so I'm trying to mock the method classPathResource.getFile() but somehow I'm not able to do so without writing PrepareForTests() and if I do that those test are not counted in JaCoCo.
I've written test case as
#Test
public void test_methodToTest() {
mockStatic(IOUtils.class);
when(IOUtils.toString(any()).thenReturn("DUMMY_STRING");
methodToTesT("file1.txt");
...
}
The problem is IOUtils.toString gets mocked properly but the call classPathResource.getFile() tries to access the file on the disk. For this, I can do this
PowerMockito.whenNew(ClassPathResource.class)
.withAnyArguments().thenReturn(mockedClassPath);
And add annotation to my test class as
#PrepareForTest(ClassToTest.class)
class MyTestClass {
...
}
But now the problem is this test class is skipped from the JACOCO test coverage . How can I write tests for this class?
You can pass a mocked reference into the constructor doing this:
class ClassToTest {
private ClassPathResource classPathResource;
public ClassToTest(ClassPathResource classPathResource) {
this.classPathResource = classPathResource;
}
public methodToTest(String input) {
IOUtils.toString(classPathResource.getFile(input));
}
}
Or you can pass the mocked reference into the method doing this:
class ClassToTest {
public methodToTest(ClassPathResource classPathResource) {
IOUtils.toString(classPathResource.getFile());
}
}
Having to mock a private member should be seen as a code smell and an indication that something is wrong with the current design. Because ClassPathResource is being initialized internal to the subject class it is now tightly coupled to that class. While not entirely impossible to mock it does make testing the class cleanly more difficult. Consider inverting the creation of the class to a delegate as a dependency.
public interface PathResource {
String getFile(String input);
}
This will allow the injection of the dependency
class ClassToTest {
private classPathResource;
public ClassToTest (PathResource resource) {
this.classPathResource = resource;
}
public String methodToTest(String input) {
return privateMethod(input);
}
private String privateMethod(String input) {
return IOUtils.toString(classPathResource.getFile(input));
}
}
and the dependency can be mocked/faked/stubbed when testing.
public void Test() {
//Arrange
//mock creation
PathResource resource = mock(PathResource.class);
String input = "path";
String expected = "expected_output";
//stubbing
when(resource.getFile(input)).thenReturn(expected);
ClassToTest subject = new ClassToTest(resource);
//Act
String actual = subject.methodToTest(input);
//Assert
verify(resource).getFile(input);
assertEquals(expected, actual);
}
in production code the ClassPathResource would be derived from the abstraction
public class ClassPathResource implements PathResource {
//...code removed for brevity
}
and it would be associated with the abstraction at the composition root.
Following the above suggestions would now allow ClassToTest to be tested in isolation without any knock on effects of implementation concerns.

How can I surpass or skip a method using JUNIT?

I have a block of code for which I need to test .Lets say
Class MainClass{
public void startProcess() {
----Some Logic to generate fileName;
uploadFile(fileName);
}
private static void uploadFile(String key) {
fileUpload();
deleteFile();
}
}
I want to write a JUNIT test which will call startProcess but either skip the uploadFile line or just ignore any lines present in uploadFile method .
I tried to use powerMock but it doesnt work . Below is my code
#RunWith(PowerMockRunner.class)
#PrepareForTest(MainClass.class)
public class MainClassTest {
#Test
public void teststartProcess() throws Exception {
processor=PowerMock.createPartialMock(MainClass.class,"uploadFile");
PowerMock.expectPrivate(processor , "uploadFile", "xyz").andAnswer(
new IAnswer<Void>() {
#Override
public Void answer() throws Throwable {
System.out.println("Invoked!");
return null;
}
}).atLeastOnce();
}
}
But it does't override the method uploadFile to just print invoked . It calls fileUpload and deleteFile instead of just skipping the lines and print invoke .
My basic goal is to mock the method uploadFile to just print
private void uploadFile(String key) {
System.out.println("Invoked");
}
I know it's possible using Mockito but we can use either PowerMock or EasyMock .
Instead of trying to go the way with over complicated tests you should consider to refactor your code so that it is testable. From what you provided I would move the file functionality to another class.
something like this:
public class MainClass {
private final FileUploader fileUploader;
public MainClass(FileUploader fileUploader) {
this.fileUploader= fileUploader;
}
public void startProcess() {
fileUploader.uploadFile(fileName);
}
}
With this refactoring you gain the possibility to use plain mocking for the test:
String fileName = "foo";
FileUploader fileUploader = mock(FileUploader.class);
MainClass classUnderTest = new MainClass(fileUploader);
classUnderTest.startProcess();
verify(fileUploader, times(1)).uploadFile(fileName);
An additional benefit is that testing the fileUploader becomes also easy. As you can see I also got rid of all the complicated Partial mocking and private testing.
You have to change the mock object to replay mode before calling the method under test and you have to tell Powermock to work on a static method:
mockStaticStrict(MainClass.class,method(MainClass.class, "uploadFile"));
PowerMock.replay(processor);
processor.startProcess();
See Mocking private methods for further information on this.
I highly suggest to refactor this code and make it testable.

JUnit test with dynamic number of tests

In our project I have several JUnit tests that e.g. take every file from a directory and run a test on it. If I implement a testEveryFileInDirectory method in the TestCase this shows up as only one test that may fail or succeed. But I am interested in the results on each individual file. How can I write a TestCase / TestSuite such that each file shows up as a separate test e.g. in the graphical TestRunner of Eclipse? (Coding an explicit test method for each file is not an option.)
Compare also the question ParameterizedTest with a name in Eclipse Testrunner.
Take a look at Parameterized Tests in JUnit 4.
Actually I did this a few days ago. I'll try to explain ...
First build your test class normally, as you where just testing with one input file.
Decorate your class with:
#RunWith(Parameterized.class)
Build one constructor that takes the input that will change in every test call (in this case it may be the file itself)
Then, build a static method that will return a Collection of arrays. Each array in the collection will contain the input arguments for your class constructor e.g. the file. Decorate this method with:
#Parameters
Here's a sample class.
#RunWith(Parameterized.class)
public class ParameterizedTest {
private File file;
public ParameterizedTest(File file) {
this.file = file;
}
#Test
public void test1() throws Exception { }
#Test
public void test2() throws Exception { }
#Parameters
public static Collection<Object[]> data() {
// load the files as you want
Object[] fileArg1 = new Object[] { new File("path1") };
Object[] fileArg2 = new Object[] { new File("path2") };
Collection<Object[]> data = new ArrayList<Object[]>();
data.add(fileArg1);
data.add(fileArg2);
return data;
}
}
Also check this example
JUnit 3
public class XTest extends TestCase {
public File file;
public XTest(File file) {
super(file.toString());
this.file = file;
}
public void testX() {
fail("Failed: " + file);
}
}
public class XTestSuite extends TestSuite {
public static Test suite() {
TestSuite suite = new TestSuite("XTestSuite");
File[] files = new File(".").listFiles();
for (File file : files) {
suite.addTest(new XTest(file));
}
return suite;
}
}
JUnit 4
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
#RunWith(Parameterized.class)
public class TestY {
#Parameters
public static Collection<Object[]> getFiles() {
Collection<Object[]> params = new ArrayList<Object[]>();
for (File f : new File(".").listFiles()) {
Object[] arr = new Object[] { f };
params.add(arr);
}
return params;
}
private File file;
public TestY(File file) {
this.file = file;
}
#Test
public void testY() {
fail(file.toString());
}
}
Junit 5 Parameterized Tests
JUnit 5 parameterized tests support this by allowing the use of a method as data source:
#ParameterizedTest
#MethodSource("fileProvider")
void testFile(File f) {
// Your test comes here
}
static Stream<File> fileProvider() {
return Arrays.asList(new File(".").list()).stream();
}
JUnit 5 DynamicTests
JUnit 5 also supports this through the notion of a DynamicTest, which is to be generated in a #TestFactory, by means of the static method dynamicTest.
import org.junit.jupiter.api.DynamicTest;
import org.junit.jupiter.api.TestFactory;
import static org.junit.jupiter.api.DynamicTest.dynamicTest;
import java.util.stream.Stream;
#TestFactory
public Stream<DynamicTest> testFiles() {
return Arrays.asList(new File(".").list())
.stream()
.map((file) -> dynamicTest(
"Test for file: " + file,
() -> { /* Your test comes here */ }));
}
The tests run in your IDE (IntelliJ here) will be displayed like this:
Should be possible in JUnit 3 by inheriting from TestSuite and overriding the tests() method to list the files and for each return an instance of a subclass of TestCase that takes the filename as constructor parameter and has a test method that tests the file given in the constructor.
In JUnit 4 it might be even easier.
You could consider using JUnitParams library, so you would have a few more (cleaner) options:
#org.junit.runner.RunWith(junitparams.JUnitParamsRunner.class)
public class ParameterizedTest {
#org.junit.Test
#junitparams.Parameters(method = "data")
public void test1(File file) throws Exception { }
#org.junit.Test
#junitparams.Parameters(method = "data")
public void test2(File file) throws Exception { }
public static File[] data() {
return new File[] { new File("path1"), new File("path2") };
}
}
#org.junit.runner.RunWith(junitparams.JUnitParamsRunner.class)
public class ParameterizedTest {
#org.junit.Test
#junitparams.Parameters(value = { "path1", "path2" })
public void test1(String path) throws Exception {
File file = new File(path);
}
#org.junit.Test
#junitparams.Parameters(value = { "path1", "path2" })
public void test2(String path) throws Exception {
File file = new File(path);
}
}
You can see more samples of usage here.
In addition about JUnitParams, why writting parameterized tests with it is easier and more readable:
JUnitParams project adds a new runner to JUnit and provides much
easier and readable parametrised tests for JUnit >=4.6.
Main differences to standard JUnit Parametrised runner:
more explicit - params are in test method params, not class fields
less code - you don't need a constructor to set up parameters
you can mix parametrised with non-parametrised methods in one class
params can be passed as a CSV string or from a parameters provider class
parameters provider class can have as many parameters providing methods as you want, so that you can group different cases
you can have a test method that provides parameters (no external classes or statics anymore)
you can see actual parameter values in your IDE (in JUnit's Parametrised it's only consecutive numbers of parameters)
If TestNG is an option, you could use Parameters with DataProviders.
Each individual file's test will have its result shown in the text-based report or Eclipse's TestNG plugin UI. The number of total tests run will count each of your files individually.
This behavior differs from JUnit Theories, in which all results are lumped under one "theory" entry and only count as 1 test. If you want separate result reporting in JUnit, you can try Parameterized Tests.
Test and inputs
public class FileTest {
#DataProvider(name="files")
public File[][] getFiles(){
return new File[][] {
{ new File("file1") },
{ new File("file2") }
};
// or scan a directory
}
#Test(dataProvider="files")
public void testFile(File file){
//run tests on file
}
}
Example output
PASSED: testFile(file1)
PASSED: testFile(file2)
===============================================
Default test
Tests run: 2, Failures: 0, Skips: 0
===============================================
I had a similar problem and ended up writing a simple JUnit 4 runner that allows med to dynamically generate tests.
https://github.com/kimble/junit-test-factory

Categories

Resources