How can I test java NIO static methods? - java

How can I test java NIO static methods?
I have a method
public Stream<String> getFileInformation(Provider provider) {
Path feedDirectoryPath;
DirectoryStream<Path> stream;
List<Path> files = new ArrayList<>();
try {
feedDirectoryPath = getFeedDirectoryPath();
if (!isFileExists(feedDirectoryPath)) {
return null;
}
stream = getDirectoryStream(feedDirectoryPath);
for(Path p : stream) {
files.add(p);
}
return Files.lines(Paths.get(files.stream().toString()));
} catch (Exception e) {
return null;
}
}
I have to test this method, but once my test arrives at Files.lines(..), I am stuck. I don't know how to assert this, and of-course if I were to pass a filePath like Path filePath = Paths.get("dummy.txt");
A NoSuchFileException would be thrown. Any help in testing this method would be greatly appreciated.

You could either supply it with an actual file (for instance in the src\test\resources folder if you're using maven), or mock the static method using PowerMock. I would recommend the first, it should be possible to load a file (for instance relative to your test class using classloader) and use this as input to the method under test.

Related

Writing Unit test with Mockito with Jgit

I am new to Mockito/PowerMockito(as it is a static method), while testing my code. I am not able to write test for this method as it contain fileList along with Jgit method, Can anyone show how can I perform testing for this perticular method.
public static String addAndCommitUntrackedChanges(final File gitFile, final String branchName,
final String commitMessage, List<String> filesList)
throws IOException, GitAPIException {
final Git openedRepo = Git.open(gitFile);
openedRepo.checkout().setCreateBranch(true).setName(branchName).call();
AddCommand add = openedRepo.add();
for (final String file: filesList) {
Path filepath = Paths.get(file); //file absolute Path
final Path repoBasePath = Paths.get("/", "tmp", "PackageName"); //file base common path
final Path relative = repoBasePath.relativize(filepath); //Remove the repoBasePath from filpath
add.addFilepattern(relative.toString());
}
add.call();
// Create a new commit.
RevCommit commit = openedRepo.commit()
.setMessage(commitMessage)
.call();
//Return the Latest Commit_Id
return ObjectId.toString(commit.getId());
}
Thanks in advance!
You should avoid static methods. How are you going to test clients of addAndCommitUntrackedChanges if you can't mock it?
To make addAndCommitUntrackedChanges more testable, introduce a GitWrapper interface:
interface GitWrapper {
Git open(File f);
}
with an implementation:
class DefaultGitWrapper implements GitWrapper {
public Git open(File f) {
return Git.open(f);
}
}
and change the signature of your method to:
public static String addAndCommitUntrackedChanges(
GitWrapper gitWrapper,
final File gitFile,
final String branchName,
final String commitMessage,
List<String> filesList)
and use GitWrapper instead of the static instance of Git.
In the particular case of Git the wrapper isn't needed, because you can just pass an instance of Git, which can be mocked normally, but when you really have a third party class which only provides static methods it's a necessary solution.
Then you can mock the things you need to mock to write a unit test, which would look something like (this is uncompiled code):
class TestAddAndCommitUntrackedChanges {
#Mock
GitWrapper gitWrapper;
#Mock
Git git;
#Mock
CheckoutCommand checkoutCommand;
#Mock
AddCommand addCommand;
#Test
public void testBehaviour() {
List<String> files = List.of("/tmp/PackageName/foo", "/tmp/PackageName/bar");
File gitFile = new File("gitFile");
when(gitWrapper.open(gitFile)).thenReturn(git);
when(git.checkout()).thenReturn(checkoutCommand);
when(checkoutCommand.setName("theBranch"))
.thenReturn(checkoutCommand);
when(git.add()).thenReturn(addCommand);
assertEquals(
"thecommitid",
addAndCommitUntrackedChanges(
gitWrapper, gitFile, "theBranch",
"the commit message", files)
);
verify(checkoutCommand).call();
verify(addCommand).addFilePattern("foo");
verify(addCommand).addFilePattern("bar");
verify(addCommand).call();
}
}
You'll also need to mock and verify the CommitCommand.

Mocking objects for JUnit test

I'm writing a Junit to test the following method in Client.java:
public FSDataInputStream getObj(String hName, Path p) throws IOException {
String myKey = pathToKey(hName, p);
FileStatus status = memoryCache.getStatus(p.toString());
if (status == null) {
status = getStatus(hName, p, "getObject");
}
if (status.isDirectory()) {
throw new FileNotFoundException("Can't open " + path
+ " because it is a directory");
}
InputStream inputStream = new InputStream(bucket, myKey,
status.getLen(), client, readAhead, inputPolicy);
return new FSDataInputStream(inputStream);
}
Initially I want to test if status == null then getStatus() is invoked and if status.isDirectory(), the FileNotFoundException is thrown
I'm new to Junit so not completely sure what I'm at but to the best of my knowledge I think I need to mock the following:
List item
Client
status
inputStream
possibly memoryCache
So far this is what I've got:
#Before
public final void before() {
private COSAPIClient myClient;
private String myBucket;
FileStatus myStatus;
InputStream myInputStream;
myClient = PowerMockito.mock(AmazonS3.class);
myInputStream = PowerMockito.mock(InputStream.class);
myFileStatus = PowerMockito.mock(FileStatus.class);
}
#Test
public void getObjTest() throws Exception {
URI uri = new URI("xyz://aa-bb-cc/data7-1-23-a.txt");
String hName = "xyz://aa-bb-cc/";
Path p = new Path("cos://aa-bb-cc/data7-1-23-a.txt");
Configuration conf = new Configuration();
myClient = spy(new Client(uri, conf));
myStatus = spy(new FileStatus());
myMemoryCache.getStatus(p.toString());
InputStream = spy(new InputStream(myBucket, objectKey, 300, myClient, 12345678910L, myInputPolicy));
}
It returns a NullPointerError at this line in my program:
FileStatus status = memoryCache.getStatus(p.toString());
I wonder is anybody could advice if/what I'm doing wronfg and how I should go about resolving this?
First, the real answer: step back for a moment. Don't start with JUnit and Mockito and your production code as input. Rather have a look into a tutorial (like here) that step-by-step explains all the relevant elements and how to "bring" them together.
In your case, the are various problems with your code:
Why are you using PowerMock? Try to go with "plain vanilla" Mockito. If your production code is so that it requires PowerMock, rather consider to rework your production instead of turning to PowerMock.
You seem to really not know where/how to apply mocking. In other words: you only mock the elements that you need to control when running your code under test. And you only use mocking, if you can't control them otherwise. Meaning: you almost never mock a list - you simply create a "normal" list to then add the things that this list should contain.
Creating a mock allows to invoke methods on that mock object. But by default, any method that returns something will return null (or maybe an empty collection, or 0 for primitive return types, see here for details). Thus you rather need a statement such as when(mockedCache.getStatus("some string")).thenReturn(someResult).

Writing Junit test to cover exception and catch block

I have written Junit test case for following function. When checked JACOCO test coverage. It is showing only try block is covered by test case. I am newbie to writing test cases. How the exceptions and catch block can be covered in test cases
Here is a method
public static List<Student> readCsvFile(String fileName)
{
BufferedReader fileReader = null;
//logic to read file
}
catch (Exception e)
{
System.out.println("Error in CsvFileReader !!!");
e.printStackTrace();
} finally
{
try
{
fileReader.close();
} catch (IOException e)
{
System.out.println("Error while closing fileReader !!!");
e.printStackTrace();
}
}
return students;
}
And TestMethod
#Test
public void ReadCsvFileTest()
{
String fileName = "test.csv";
List<Student> result = new ArrayList<Student>();
result = CsvFileReader.readCsvFile(fileName);
Student student1 = null;
Iterator<Student> it = result.iterator();
while (it.hasNext())
{
Student s = it.next();
if ("471908US".equals(s.getId()))
{
student1 = s;
break;
}
}
assertTrue(student1 != null);
}
In such situations you may often consider the introduction of additional dependencies to your class. Here is what I mean with a rough example. Create a factory for readers:
interface BufferedReaderFactory
{
public BufferedReader createBufferedReader(String fileName) throws IOException;
}
Then you will have a trivial implementation that hardly needs any testing, e.g. something similar:
class BufferedReaderFactoryImpl implements BufferedReaderFactory
{
#Override
public BufferedReader createBufferedReader(String fileName) throws IOException
{
return new BufferedReader(new FileReader(fileName));
}
}
Then you have to find a way to inject this dependency into your class. I usually use Guice in my daily work but you may try something as simple as using constructor injection and making your method non static. Here is an example:
class CsvFileReader
{
private final BufferedReaderFactory factory;
public CsvFileReader(BufferedReaderFactory factory)
{
this.factory = factory;
}
public List<Student> readCsvFile(String fileName)
{
BufferedReader fileReader = null;
try
{
fileReader = factory.createBufferedReader(fileName);
...
}
catch(IOException e)
{
...
}
finally
{
...
}
return new LinkedList<>();
}
}
With a mocking framework like Mockito the behavior of this class in case of IOException-s is easier to test now (note that you may also return mocks that throw exceptions from the factory). Here is a sample:
#RunWith(MockitoJUnitRunner.class)
public class MyTest
{
#Mock
private BufferedReaderFactory mockFactroy;
#Test
public void testIOException() throws IOException
{
String ivalidFileName = "invalid.txt";
//throw exception in case that invalid file name is passed to the factory
Mockito.when(mockFactroy.createBufferedReader(ivalidFileName)).thenThrow(new IOException("Hello!"));
CsvFileReader csvFileReader = new CsvFileReader(mockFactroy);
//invoke with a factory that throws exceptions
csvFileReader.readCsvFile(ivalidFileName);
//...
//and make a sensible test here, e.g. check that empty list is returned, or proper message is logged, etc.
}
}
You may do that without Mockito, of course - by implementing a test factory. But this is more cumbersome especially in more complicated use cases. Once the IOException is thrown you will get appropriate coverage report by JaCoCo.
Also mind a limitation of JaCoCo mentioned here, in section Source code lines with exceptions show no coverage. Why?
Given the current signature of your method under test, getting to full coverage isn't easy: your catch block is only executed when an exception is thrown within your try block.
One way to solve this: do not pass in the file name, but the reader object itself. Like:
public static List<Student> readCsvFile(String fileName) {
return readCsvFile(new BufferedReader(fileName));
}
static List<Student> readCsvFile(BufferedReader reader) {
try {
...
} catch( ...
Now you can write several specific unit tests for that second method. You keep your tests that simply do "correct" reading; but you add one where you pass in a mocked reader object ... that simply throws an Exception at some point. Please note that I made that new method just package protected - you probably don't want to use that "public"; and making it private would prevent it from being unit tested.
That should help you achieving full coverage. Of course you will also need at least one test to "cover" the string-taking method, too.
Some notes:
Be careful about re-inventing the wheel. There are many existing CSV parsers already. And be assured: writing a correct CSV parser that is able to deal with all "correct" input CSV is much harder than it sounds. If this is not for "learning purposes" I strongly advise to not write your own CSV parser.
Be careful about making such things static. As said, a real CSV parser is a complicated thing, and worth its complete own class. So no static helper methods - a normal class which you instantiate to then call methods on it (that would also for using dependency injection which would help with the problem you are asking about ... getting exceptions thrown within try blocks)
You are catching Exception in your code example. Don't do that - try to catch exactly those exceptions that your code can actually produce (probably IOException in your case).

Writing unit tests with mocking of static methods in java

I'm new to writing tests in general. The class that I need to test has one method and it needs to be tested:
public String run(final Map<String, Dataset> datasets)
throws ApiException {
final String sourcePath = ElementsUtil.getElementFromDatasets(inputElementNames.get(0), datasets).getValue();
final String destinationPath = ElementsUtil.getElementFromDatasets(inputElementNames.get(1), datasets).getValue();
final File source = new File(sourcePath);
final File destination = new File(destinationPath);
if (source.exists()) {
if (source.isDirectory()) {
final IOFileFilter filter = new WildcardFileFilter(pattern);
final Iterator<File> it = FileUtils.iterateFiles(source, filter, null);
while (it.hasNext()) {
final File file = it.next();
moveFileToDirectory(file, destination);
}
} else {
moveFileToDirectory(source, destination);
}
} else {
LOGGER.error("Source file/folder at path {} doesn't exist.", sourcePath);
}
return "0";
}
At first, with my limited knowledge of writing unit tests, my unit test looked like this:
#Test(description = "Test in case the source is a file.")
public void moveFileTest1() {
// setup
final String fileName = UUID.randomUUID().toString() + ".txt";
final String folderName = UUID.randomUUID().toString();
final Element source = new Element("source", "./" + fileName);
final Element destination = new Element("destination", "./" + folderName);
...
final Path sourcePath = Paths.get(source.getValue());
final Path destinationPath = Paths.get(destination.getValue());
final Path fileDestination = Paths.get(destination.getValue() + "/" + fileName);
try {
Files.createFile(sourcePath);
Files.createDirectory(destinationPath);
// exercise
moveFile.run("", datasets, null);
// verify
Assert.assertEquals(Files.exists(fileDestination), true);
Assert.assertEquals(Files.exists(sourcePath), false);
} catch (ApiException | IOException e) {
LOGGER.error("Exception : ", e);
} finally {
// teardown
try {
Files.deleteIfExists(sourcePath);
} catch (final IOException e) {
LOGGER.error("Exception in teardown: ", e);
}
try {
Files.deleteIfExists(fileDestination);
} catch (IOException e) {
LOGGER.error("Exception in teardown: ", e);
}
try {
Files.deleteIfExists(destinationPath);
} catch (IOException e) {
LOGGER.error("Exception in teardown: ", e);
}
}
}
After reading some articles about unit testing I found out that my test isn't exactly testing a unit since my method depends on different util methods. Also I found out about mocking objects in tests and how everything should be mocked. My question is: Should I use mocking in each of these util methods/new object calls etc. or is there a different approach? How would you test this piece of code?
What you are doing is called integration testing. Integration testing is testing an object/method without mocking nothing, on real data or data that you produce during the test itself. What integration testing does is to test both your unit, the units that your unit uses and the flow of the use. If you would like to test only your unit you should mock all other units that your unit uses and create flows of those units doing their job successfully or not. Meaning you should replicate a test in which a used unit throws an exception/returns a value that you don't expect as a good value(only if it really can do it) and return a value that you do expect and now how to work with.
Usually when writing tests you do both kinds of testing, unit and integration tests
There is such good test principle as "Don't Mock What You Don't Own". What doesn't it mean? It means your shouldn't mock/stub any interface which you cannot control, even if this interface has been written by your company, but not by your team.
But what about writing unit tests, not integration tests, where you may want to mock all classes except a class which is being tested? Indeed, this is a really tricky question. And an answer more about system design than about testing. You may read about how the issue could be resolved here and here.
What does it means for your?
As your mentioned ElementsUtil has been written by you, so this class exactly should be mocked. How? It depends is it the legacy code or new code which you're writing now. If you have legacy code - then you need PowerMock, otherwise you may change design and use an instance of ElementsUtil.
For example, divide the ElementsUtil class into three classes: Elements- interface, ElementsImpl- implementation, ElementsUtil - class with static access to keep compatibility. The ElementsUtil may have method
public static Elements getInstance()
And the method could be used by class which contains run method in constructor. But you may provide either constructor with parameter or setter. By the way, as I remember Mockito can inject mocks into private fields. After this refactoring you don't need PowerMock and can use only Mockito.
Now about FileUtils. The class doesn't belong to you, so if follow good practice, then this class should be mock. But FileUtils works with file and it will already be integration tests. So answer - the FileUtils should be wrapped by a new your class.

Using JMockit to return actual instance from mocked constructor

I've looked at the following question and it is not the same as mine:
jMockit: How to expect constructor calls to Mocked objects?
This question is similar but the answer is not helpful to me:
How to mock the default constructor of the Date class with JMockit?
What I am trying to do is mock a constructor call to java.util.zip.ZipFile, specifically the one that has a java.io.File argument. I would like for the constructor to return an instance of a different ZipFile, one I will instantiate with the constructor that only takes a String argument.
This constructor call takes place inside a method under test, so I can't inject the ZipFile I want as a parameter.
For example, the code looks something like this:
public void whatever() {
//some code
//some more code
foo();
//yet more unrelated code
}
private Blah foo() {
ZipFile zf;
//a bunch of code we don't care about
zf = new ZipFile(someFile);// I want to give it a known zipfile! mock this!
// some more code we don't care about
Enumeration<?> entries = zf.entries();
ZipEntry entry = (ZipEntry) entries.nextElement();
InputStream is = zf.getInputStream(entry)
//maybe some other calls to the ZipFile
// do something else
}
My first thought was to do the following with static partial mocking:
final ZipFile test = new ZipFile("path/to/actual.zip");
new NonStrictExpectations() {
#Mocked("(java.io.File)")
ZipFile zf;
{
new ZipFile((File) any); result = test;
}
};
But this won't work as indicated by this line in the tutorial: constructors have void return type, so it makes no sense to record return values for them
My second thought was to try the following:
new NonStrictExpectations() {
{
newInstance("java.util.zip.ZipFile", new File("path/to/actual.zip"));
}
};
But this throws the following when trying to initialize the file:
java.util.zip.ZipException: error in opening zip file
at java.util.zip.ZipFile.open(Native Method)
at java.util.zip.ZipFile.<init>(Unknown Source)
at java.util.zip.ZipFile.<init>(Unknown Source)
My third thought was to use a #MockClass as below:
#Before
public void setUp() throws Exception {
Mockit.setUpMocks(MockedZipFile.class);
}
#After
public void tearDown() {
Mockit.tearDownMocks();
}
#MockClass(realClass=ZipFile.class)
public static class MockedZipFile {
public ZipFile it;
#Mock
public void $init(File f) throws ZipException, IOException {
it = new ZipFile("path/to/actual.zip");//this is what would be called
}
}
But this hoses some other mocks I have that load a configuration file for a different part of my test class. Not to mention I will want different zip files for different test cases.
I suppose I could mocking everything the ZipFile would do, but this would quickly become a giant pain as it's called lots of places, it's output would be need to be mocked, etc, etc. Refactoring to try to make this accessible would be awkward, as the code that uses the ZipFile is internal to the code and the public methods don't really care about it.
I have a feeling there is a way for JMockit to allow this (giving a particular instance of an object when a constructor is called), but I can't figure it out. Does anyone have any ideas?
EDIT: I tried the method suggested by #Rogerio, but I have a new error. Here's my setup:
final ZipFile test = new ZipFile("path/to/actual.zip");
new NonStrictExpectations() {
ZipFile zf;
{
zf.entries();
result = test.entries();
zf.getInputStream((ZipEntry) any);
result = new Delegate() {
InputStream getInputStream(ZipEntry entry) throws IOException {
return test.getInputStream(entry);
}
};
}
};
but I get the following stack trace:
java.lang.InternalError
at path.to.test.ExtractDataTest$1.<init>(ExtractDataTest.java:61)
at path.to.test.ExtractDataTest.setUp(ExtractDataTest.java:61)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:49)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
where line 61 is the new NonStrictExpectations() { line.
I really want to say "instead of mocking this object, substitute this other object of the same type". Maybe I have expressed that poorly.
EDIT2: I figured I should include version numbers:
Using Eclipse 3.6.1
Java 1.6.0_26
JMockit 0.999.10
JMockit can mock the ZipFile class, but it interferes with class loading since the JarFile subclass is used by the JVM all the time (whenever it loads a class from a jar file in the classpath). Currently, there is no easy way to avoid this interference (there is a plan to "fix" this, but it will take time).
However, this particular test case isn't very suited for a mocking tool anyway. Instead, I would recommend setting up the test so that it provides an actual zip file with the desired contents in the proper place.
(another edit)
I just applied a change to JMockit (for release 0.999.12) which allows the following test to pass, provided there is a test.zip file in the working dir, and it contains a text file whose first line is "test":
#Test
public void mockZipFile() throws Exception
{
final ZipFile testZip = new ZipFile("test.zip");
new NonStrictExpectations() {
#Capturing #Injectable ZipFile mock;
{
mock.entries(); result = testZip.entries();
mock.getInputStream((ZipEntry) any);
result = new Delegate() {
InputStream delegate(ZipEntry e) throws IOException {
return testZip.getInputStream(e);
}
};
}
};
ZipFile zf = new ZipFile("non-existing");
ZipEntry firstEntry = zf.entries().nextElement();
InputStream content = zf.getInputStream(firstEntry);
String textContent = new BufferedReader(new InputStreamReader(content)).readLine();
assertEquals("test", textContent);
}
However, I would still recommend not using a mocking API for cases like this. Instead, use a real file.
This probably won't help you, but if you were using Mockito or EasyMock, you could add PowerMock, which allows you to mock the construction of new objects in your code under test.

Categories