Simulate File in Java - java

I'm trying to write unit tests for a method that takes a String filename, then opens the file and reads from it. So, to test that method, I thought about writing a file, then calling my method. However, in the build farm, it is not possible to write files arbitrarily to disk. Is there a standard way to "simulate" having a real file in my unit test?

I've found that Mockito and Powermock are a good combination for this. Actually there's a blog post with a sample, where the File-class's constructor is mocked for testing purposes. Here's also a small example I threw together:
public class ClassToTest
{
public void openFile(String fileName)
{
File f = new File(fileName);
if(!f.exists())
{
throw new RuntimeException("File not found!");
}
}
}
Testing with Mockito + Powermock:
#RunWith(PowerMockRunner.class)
#PrepareForTest(ClassToTest.class)
public class FileTest
{
#Test
public void testFile() throws Exception
{
//Set up a mocked File-object
File mockedFile = Mockito.mock(File.class);
Mockito.when(mockedFile.exists()).thenReturn(true);
//Trap constructor calls to return the mocked File-object
PowerMockito.whenNew(File.class).withParameterTypes(String.class).withArguments(Matchers.anyString()).thenReturn(mockedFile);
//Do the test
ClassToTest classToTest = new ClassToTest();
classToTest.openFile("testfile.txt");
//Verify that the File was created and the exists-method of the mock was called
PowerMockito.verifyNew(File.class).withArguments("testfile.txt");
Mockito.verify(mockedFile).exists();
}
}

If you use JUnit, there is the TemporaryFolder. Files are deleted after the test. Example given on the page:
public static class HasTempFolder {
#Rule
public TemporaryFolder folder= new TemporaryFolder();
#Test
public void testUsingTempFolder() throws IOException {
File createdFile= folder.newFile("myfile.txt");
File createdFolder= folder.newFolder("subfolder");
// ...
}
}
However, I have also used it for testing my Android class read/write capabilities like:
[...]
pw = new PrintWriter(folder.getAbsolutePath() + '/' + filename);
pw.println(data);

How about using mocked stream classes, which override the real ones (like BufferredReader, File) completely (meaning all methods or all methods you use)? The data can be saved as an array of bytes, for example, in some singleton, if they are to be used between different test classes.

This is highly frowned upon:
The smallest amount of testable code. Often a single method/function,
sans the use of other methods or classes. Fast! Thousands of unit
tests can run in ten seconds or less! A unit test NEVER uses:
a database
an app server (or server of any kind)
file/network I/O or file system;
another application;
the console (System.out, System.err, etc.)
logging
most other classes (exceptions include DTO's, String, Integer, mocks and maybe a few others). "
Source
If you must read from a file, have a test file pre-generated that all unit tests read from. There is no need to write anything to disk.

Related

Prevent special class from static mocking

I have a class named SpecialClass. This class has the method doSomething in it. This class is in this particular manner special, that I do not want to mock this class at all via MockedStatic. It is very easy for me to remeber that I don't want to mock this class, but there are other people programming at the same project and they should know to not mock this class but I can not relate on it.
Given this special test:
#Test
void testSomething(){
try(MockedStatic<SpecialClass> mock = Mockito.mockStatic(SpecialClass.class)){
mock.when(SpecialClass::doSomething).thenReturn(0);
System.out.println(SpecialClass.doSomething());
}
}
I dont want to let this test execute at all. But it will execute, because Mockito does not know about my SpecialClass and not to mock it. There could be also various other locations where SpecialClass could be mocked.
Some classes can not be mocked because of Mockito decides so. Given this mock of java.lang.System:
#Test
void testFail(){
try(MockedStatic<System> sys = Mockito.mockStatic(System.class)){
// some code
}
}
I get the exception org.mockito.exceptions.base.MockitoException: It is not possible to mock static methods of java.lang.System to avoid interfering with class loading what leads to infinite loops, which is ok.
My question is: Can I somehow have some global project settings (or some annoations) to mark my class SpecialClass to be not used in Mockitos MockedStatic? I would be also ok if I can also not mock this class in a normal way.
I tried to find some information regarding restricting mocks, but i was unable to find some.
My mockito version of mockito-core and mockito-inline is 4.3.1
I used a junit test to read all my files and trying to detect all mocks.
Please note that my test is working in a very special assumption that all of my test classes are in the same directory. This will hoever not be given in every project. In this case you will need to find the files recursivly.
class NoStaticMockTest {
#Test
void testNoMocks() throws IOException {
// Base dir of my tests
File testDir = new File("src/test/java");
// reading all Files in the test directory excluding this file
for (File file : testDir.listFiles(pathname -> !pathname.getAbsolutePath().endsWith("NoStaticMockTest.java") && pathname.isFile())) {
try (FileInputStream fis = new FileInputStream(file)) {
// Reading the contennt and trying to find MockedStatic<SpecialClass>
String content = IOUtils.toString(fis);
if (content.contains("MockedStatic<SpecialClass>")) {
Assertions.fail(file.getAbsolutePath() + " used MockedStatic<SpecialClass> which is not allowed");
}
}
}
}
}

Can i mock file.mkdirs() which is inside of a method

Here is my legacy code below. I don't want to create an actual folder. Instead I want to create a temp folder, which has to be deleted after test execution is over.
Public class MainClass() {
public void m1() {
m2();
}
private void m2() {
File f=new File();
f.mkrdirs()
}
}
Here is my TestClass
public class TestClass{
#Rule
public TemporaryFolder folder= new TemporaryFolder();
public void testM1(){
File fileSpy = mock(File.class);
Mockito.doNothing().when(fileSpy.mkdir());
}
}
but not working.Any clues?
Your problem starts here:
File f=new File();
Your production code calls new for the file class.
In your test code, you do:
File fileSpy = mock(File.class);
And how is that spy related to your f instance? Answer: not at all.
Declaring a spy in your test doesn't magically make your production code use it! So you have to make sure that when your production code uses that f object ... f should be your fileSpy
Now you have these options:
see if you can change your production code, so that you can somehow pass in a file instance. Either by making it an argument, that you really can pass something to, or by turning f into a field of your class, to then use Mockito's #InjectMock annotation
if you are stuck with your production code, Mockito won't do. Then you need PowerMock(ito) or JMockit.
Typically, I advise people to stick with plain Mockito, and avoid mocking new() (even more so mocking static calls) via PowerMock(ito) like the plague.
But testing legacy code is the one situation where you have to choose between "not testing at all" or "testing with PowerMock(ito)". So, if you can't change your production code, then your only choice would be to mock that call to new(). See here for how to do that.

Specific setup for each junit test

I have a sequence of tests which have to be fed an input data in the form of a file.However,the exact data content to be fed into each would be specific.
I intend to use temporary files to achieve this.
The Setup method does not take a parameter.
SO ,what could be done so that the setup can be made to read a specific fragment for each specific test.
The actual set of steps in Setup would be same - creating a temporary file,but with a specific tailored piece of data.
Setup methods (i.e., methods annotated with #Before) are designed for running the same steps before every test case. If this isn't the behavior you need, just don't use them.
At the end of the day, a JUnit test is just Java - you could just have a method that takes a parameter and sets up the test accordingly and call it explicitly with the different arguments you need:
public class MyTest {
private void init(String fileName) {
// Reads data from the file and sets up the test
}
#Test
public testSomething() {
init("/path/to/some/file");
// Perform the test and assert the result
}
#Test
public testSomethingElse() {
init("/path/to/another/file");
// Perform the test and assert the result
}
}

How to test a method which creates directories and file system

This is my method, which creates a file system when gets called.
/**
* Process all messages from {#code messagesDir} directory and save results in {#code resultsDir}
*
* #param messagesDir messages directory
* #param resultsDir results directory
* #param logDirectory directory where should be written logs
* #param cleanupResultDir whether cleanup result directory
* #throws PtfProcessorException if provided {#code messagesDir} is invalid directory or there was comparision exception
*/
public void processMessages(String messagesDir, String resultsDir, String logDirectory, boolean cleanupResultDir)
throws PtfProcessorException {
// check if the directories are valid
checkIsValidDirectory(messagesDir);
checkIsValidDirectory(resultsDir);
// get operations directories
final File[] operationsDirsFiles = getInnerDirs(messagesDir);
// get results directory and clean up it if cleanupResultDirectory is true
final File resultsDirFile = new File(resultsDir);
if (cleanupResultDir) {
cleanUpDirectory(resultsDirFile);
}
// compare messages by operation bases
for (File operationDirFile : operationsDirsFiles) {
// operation log directory file
final File logOperationDirFile = new File(logDirectory, operationDirFile.getName());
try {
compareOperationMessages(operationDirFile, resultsDirFile, logOperationDirFile);
} catch (PtfProcessorException e) {
writeExceptionLog("Exception while comparing operation: " + operationDirFile.getName(),
e, logOperationDirFile);
}
}
}
Now I must write a unit test for this method. I have read several posts related to "TemporaryFolder" in Mockito and "Rule" annotation, but to tell the the truth, I do not know which approach I need. Anybody please help me to find a way for testing this method.
If there is a necessity for providing the helper methods used in aforementioned method, I can provide them.
One of the aproaches may be to create wrapper class for File and then provide it into the class that this method resides. Then you will be able to mock it with mockito and verify whether proper methods were invoked.
Normally you should not test classes that are not in your project, (File for example). This is not a good practice while writing unit tests. The thing that you can test is behavior of your method, that is whether proper external methods were invoked.
You should also remember to check if the validation is working by providing various inputs in your unit tests.
I suggest to use JUnit's TemporaryFolder rule.
public class YourTest {
#Rule
public final TemporaryFolder folder = new TemporaryFolder();
#Test
public void verifySomeFileBehaviour() throws Exception {
File messagesDir = folder.newFolder();
File reportsDir = folder.newFolder();
File logsDir = folder.newFolder();
//create your object and then
yourObject.processMessages(
messagesDir.getAbsolutePath(),
reportsDir.getAbsolutePath(),
logsDir.getAbsolutePath(),
true
);
//now you can verify the contents. E.g.
assertTrue( new File( messagesDir, "firstMessage.txt" ).exists() );
}
}
I recommend to use AssertJ as assertion library because it has nice assertions for files.
The real issue here: you created hard to test code - because you are implementing more than one responsibility in this method. Instead of collecting file names, and then creating them, your method could for example look like this:
public void processMessages(String messagesDir, String resultsDir, String logDirectory, boolean cleanupResultDir)
throws PtfProcessorException {
directoryValidator.validate(messagesDir, resultsDir);
final File[] operationsDirsFiles = getInnerDirs(messagesDir);
cleanupService.cleanup(cleanupResultDir, resultsDirFile);
fileService.create(operationsDirsFiles)
The above is merely meant as "pseudo code" to give you some ideas. The core point is: by extracting behavior into its own class/interface you enable yourself to test each functionality separately. And later on, you can use dependency injection to make that functionality available to more complex methods. You see - you could now mock all those "service" objects; and all of a sudden you only talk about verifying that you pass a certain list of File objects to another method.
Long story short: as usual the answer is: write testable code. Follow clean code rules, such as single responsibility principle. And when you do that, the quality of your production code will improve; and your tests will be much simpler to write down. Without the need to do fake file system operation all over the place.

jUnit4.0 : how to know current test method name?

I've implemented a feature in my jUnit tests that takes, for every test case, a fresh copy of a data source. This copy is taken in a folder specific for each test case. The idea is that every test case can start from a clean situation, manipulate it and let it as such after the run. This is often useful when the test fails for analysing the problem.
For now I have to call this feature directly in the test method because I don't know how to retrieve the current test name:
public void testTest1() {
TestHelper th=TestHelper.create("testTest1",subPathToDataSource);
// do the test...
Path dataPath = th.getDataPath();
...
}
I would like to be able to write something like this:
Path dataPath;
#Before
public initTest() {
th=TestHelper.create(SomeJUnitObject.getCurrentTestName(),subPathToDataSource);
...
}
public void testTest1() {
// do the test...
Path dataPath = th.getDataPath();
...
}
Until now I found as answers : "You don't need to know that"... But I do need it !
Is this possible ?
Kind regards
Look at the TestName rule.
You should be able to add in your test class:
#Rule TestName name=new TestName();
And then access it.
(On phone, so can't check versions support/details - might be 4.x only)
Here is an alternative approach; create an abstract class which your "real" test classes inherit.
I have several such examples in my projects and here I will give one, mainly testing for individual JSON Patch operations.
All my test files are JSON, and located under an appropriately named resource directory. The base, abstract class is JsonPatchOperationTest. And here is the full code of AddOperationTest which tests for JSON Patch's add operation:
public final class AddOperationTest
extends JsonPatchOperationTest
{
public AddOperationTest()
throws IOException
{
super("add");
}
}
And that's it! Not even one test method in this class, but of course your implementation may vary.
In your case you probably want to pass the directory name as a constructor argument, or the like.

Categories