Test Class using File and FileInputStream with Mockito/PowerMockito [duplicate] - java

This question already has answers here:
How do I unit-test saving file to the disk?
(3 answers)
Closed 8 years ago.
I would like to mock a File and FileInputStream Object for an JUnit test.
Let us say we are having a Parser. From outside the Parser is just accessible via the parse(File myFile) method. The other methods like readMyStream....are private.
Example Code:
public class Parser {
public HashMap<String, String> parse(File myFile) throws Exception {
HashMap<String, String> myConfig;
Config config;
try {
//this line gives me a headache
FileInputStream myFileInputStream = new FileInputStream(myFile);
configStream = readMyStream(myFileInputStream);
..........
} catch (FileNotFoundException e) {
throw e;
}
return myConfig;
}
//reads the stream
private Config readMyStream(FileInputStream myFileInputStream) {
Config config;
...JDOM stuff....
return config;
}
}
The problems I face:
how to assert a File Object an FileInputStream (PowerMockito) like this File belongs to this FileInputStream with the following content
how to mock a private method (Mockito/PowerMockito)
an example of the Mocking/not working :)....
public class ParserTest {
#Test
public final void testParse() {
File MOCKEDFILE = PowerMockito.mock(File.class);
PowerMockito.when(MOCKEDFILE.exists()).thenReturn(true);
PowerMockito.when(MOCKEDFILE.isFile()).thenReturn(true);
PowerMockito.when(MOCKEDFILE.isDirectory()).thenReturn(false);
PowerMockito.when(MOCKEDFILE.createNewFile()).thenReturn(true);
PowerMockito.when(MOCKEDFILE.length()).thenReturn(11111111L);
//what about the path of MOCKEDFILE which isn't existing
PowerMockito.when(MOCKEDFILE.getPath()).thenReturn(?????);
//how to assign a File an FileInputStream? (I thought something like)
PowerMockito.mockStatic(FileInputStream.class);
FileInputStream MOCKEDINPUTSTREAM = PowerMockito.mock(FileInputStream.class);
PowerMockito.whenNew(FileInputStream.class).withArguments(File.class).thenReturn(MOCKEDINPUTSTREAM);
//how to mock the private method readMyStream
}

Rather than mocking File, I would suggest using the TemporaryFolder Rule and create / not create the file as appropriate.
In my previous projects, we have written a FileInputStreamSupplier class that is used to create a FileInputStream. This class can then be mocked to provide a mocked FileInputStream to allow for testing behavior. Generically, you could have your class take a Supplier<FileInputStream> (using Guava) and mock that.

Related

How can I test java NIO static methods?

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.

How to test this file-writing, 3lines function?

This is my method in some service class. It's public so it should be tested. I simply do not know WHAT should I test. I'd mock Writer and spyOn function call, but with this implementation it's impossible (isn't it?)
I'm using Mockito and JUnit
For now, I can only make function to throw and assert that exception
Any help?
#Override
public void initIndexFile(File emptyIndexFile) {
try {
Writer writer = new FileWriter(emptyIndexFile);
writer.write("[]");
writer.close();
} catch (IOException e) {
throw new IndexFileInitializationException(
"Error initialization index file " + emptyIndexFile.getPath()
);
}
}
If you feel that adding the the special content is the business logic and therefore the responsibility of your class, then creating the FileWriter is not (according to the single responsibility pattern.
So you should use a FileWriterFactory that is injected into your Class under Test. Then you can mock that FileWriterFactory to return a mock implementation of the Writer interface on which in turn you can check that it got the expected String.
Your CuT would change to this:
private final WriterFactory writerFactory;
public ClassUnderTest(#Inject WriterFactory writerFactory){
this.writerFactory = writerFactory;
}
#Override
public void initIndexFile(File emptyIndexFile) {
try {
Writer writer = writerFactory.create(emptyIndexFile);
writer.write("[]");
writer.close();
} catch (IOException e) {
throw new IndexFileInitializationException(
"Error initialization index file " + emptyIndexFile.getPath()
);
}
}
and your test to this:
class Test{
#Rule public MockitoRule mockitoRule = MockitoJUnit.rule();
#Mock
private FileWriterFactory fileWriterFactory;
private Writer fileWriter = spy(new StringWriter());
File anyValidFile = new File(".");
#Test
public void initIndexFile_validFile_addsEmptyraces(){
//arrange
doReturn(fileWriter).when(fileWriterFactory).create(any(File.class));
// act
new ClassUnderTest(fileWriterFactory).initIndexFile(anyValidFile);
//assert
verify(fileWriterFactory)create(anyValidFile);
assertEquals("text written to File", "[]", fileWriter.toString());
verify(fileWriter).close();
}
}
in addition you could easily check that your CuT intercepts the IOException:
#Rule
public ExpectedException exception = ExpectedException.none();
#Test
public void initIndexFile_missingFile_IndexFileInitializationException(){
//arrange
doReturnThrow(new IOException("UnitTest")).when(fileWriterFactory).create(any(File.class));
//assert
exception.expect(IndexFileInitializationException.class);
exception.expectMessage("Error initialization index file "+anyValidFile.getPath());
// act
new ClassUnderTest(fileWriterFactory).initIndexFile(anyValidFile);
}
Nice! a factory just to test 3 lines of code! – Nicolas Filotto
This is a good point.
The question is: will there be any method within that class ever interacting with the File object directly and needs to create the FileWriter afterwards?
If the answer is "no" (as it is most likely) following the KISS principle you should inject a Writer object directly instead of the factory and have your methods without the File parameter.
private final Writer writer;
public ClassUnderTest(#Inject Writer writer){
this.writer = writer;
}
#Override
public void initIndexFile() {
try {
writer.write("[]");
writer.close();
} catch (IOException e) {
throw new IndexFileInitializationException(
"Error initialization index file " + emptyIndexFile.getPath()
);
}
}
modified test:
class Test{
#Rule public MockitoRule mockitoRule = MockitoJUnit.rule();
#Rule public ExpectedException exception = ExpectedException.none();
#Mock
private FileWriterFactory fileWriterFactory;
#Mock
private Writer failingFileWriter;
private Writer validFileWriter = spy(new StringWriter());
File anyValidFile = new File(".");
#Test
public void initIndexFile_validFile_addsEmptyraces(){
//arrange
// act
new ClassUnderTest(validFileWriter).initIndexFile();
//assert
verify(fileWriterFactory)create(anyValidFile);
assertEquals("text written to File", "[]", fileWriter.toString());
verify(fileWriter).close();
}
#Test
public void initIndexFile_missingFile_IndexFileInitializationException(){
//arrange
doReturnThrow(new IOException("UnitTest")).when(failingFileWriter).write(anyString());
//assert
exception.expect(IndexFileInitializationException.class);
exception.expectMessage("Error initialization index file "+anyValidFile.getPath());
// act
new ClassUnderTest(fileWriterFactory).initIndexFile(anyValidFile);
}
}
To test that your method can interact with a writer correctly, by sending the correct commands, your pogram has to expose some sort of "seam" so that your test can configure a mock FileWriter. I'm not familiar with mockito but one way would be to encapsulate the FileWriter instantiation behind a method then your test could override that method to return a mock FileWriter.
Assuming that File is an interface:
public Writer getFileWriter(File emptyIndexFile) {
return new FileWriter(emptyIndexFile);
}
This could allow you to override the above method for a test and return a fake Writer
#Override
public Writer getFileWriter(File emptyIndexFile) {
return mockFileWriterInstance;
}
Then your test could make exercise initIndexFile and make assertions on the operations. Using a mock file writer shoudl be trivial to throw IOException so that you can exercise error handling logic.
You could simply provide a temporary file to your method in your test and simply check that it contains [] as expected and once over delete the file.
Something like:
public class FileWritingTest {
// File to provide to the method initIndexFile
private File file;
/* This is executed before the test */
#Before
public void init() throws IOException {
// Create a temporary file
this.file = File.createTempFile("FileWritingTest", "tmp");
// Indicates that it should be removed on exit
file.deleteOnExit();
}
/* This is executed after the test */
#After
public void clean() throws IOException {
// Delete the file once test over
file.delete();
}
#Test
public void testInitIndexFile() throws IOException {
FileWriting fw = new FileWriting();
// Call the method
fw.initIndexFile(this.file);
// Check that the content is [] as expected
Assert.assertEquals("[]", new String(Files.readAllBytes(file.toPath())));
}
}
NB 1: I rely on new String(byte[]) which means that I rely on the default character encoding like you do in your current code but it is not a good practice, we should set a character encoding explicitly to avoid platform dependent.
NB 2: Assuming that you use java 7 or higher, you should consider using the try-with-resources statement to properly close your writer, your code would then be:
public void initIndexFile(File emptyIndexFile) {
try (Writer writer = new FileWriter(emptyIndexFile)) {
writer.write("[]");
} catch (IOException e) {
throw new IndexFileInitializationException(
"Error initialization index file " + emptyIndexFile.getPath()
);
}
}
Mocking a dependency is possible and natural, but mocking an object declared in the body of the method is not natural and tricky.
I imagine 3 solutions:
1) Why, instead of mocking, could you not simply assert that the file is written with the expected character?
It avoids tricks, but it may be redundant and slow if you perform this task very often and you want to unit test them.
2) Making the local variable an instance field to mock it. This seems really a not at all clean solution. If you have multiple methods in the same class that does this kind of processing, you risk to reuse the same writer or to have multiple writer fields. In both cases, you could have side effects.
3) If you perform many write operations and you want to really isolate the call to the writer, you have a solution: redesign your code to have a testable class.
You could extract a dependency to perform the writer processings. The class could provide a method with required parameters to perform instructions. We could call it : WriteService.
public class WriteService {
...
public void writeAndClose(Writer writer, String message){
try {
writer.write(message);
writer.close();
}
catch (IOException e) {
throw new IndexFileInitializationException("Error initialization index file " + emptyIndexFile.getPath());
}
}
}
This class is testable because the writer dependency is a parameter.
And you call the new service like that :
public class YourAppClass{
private WriteService writeService;
public YourAppClass(WriteService writeService){
this.writeService=writeService;
}
#Override
public void initIndexFile(File emptyIndexFile) {
Writer writer = new FileWriter(emptyIndexFile);
writeService.writeAndClose(writer,"[]");
}
}
Now initIndexFile() is also testable by mocking WriteService.
You could check tat writeAndClose() is called on writeService with the good parameter.
Personally, I would use the first solution or the third solution.

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).

How to load a properties file in java without calling laod method separately

How to load a properties file in java without calling laod method separately
i want to load the file while instantiation of the properties object itself.
like i have pasted below but i am not able to succeed in it.
class test{
Properties configFile = new Properties(load(new FileInputStream("config.properties"));
}
Just create a separate method to do that - potentially in a helper class you can use elsewhere:
public class PropertiesHelper {
public static Properties loadFromFile(String file) throws IOException {
Properties properties = new Properties();
FileInputStream stream = new FileInputStream(file);
try {
properties.load(stream);
} finally {
stream.close();
}
return properties;
}
}
Note that due to the possibility of an IOException, you'll still need to be careful where you call this from. If you want to use it in an instance initializer, you'll need to declare that all of your constructors can throw IOException.
Something along the lines of this:
class Test {
Properties configFile = new Properties() {{ load(new FileInputStream("config.properties")); }};
}
You are actually sub classing Properties here and using its initialization section.
load(..) might throw an Exception if so you would need to add a try { ... } catch () {}

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