Mockito - Mocking behaviour of a File - java

I have a class that takes in a single file, finds the file related to it, and opens it. Something along the lines of
class DummyFileClass
{
private File fileOne;
private File fileTwo;
public DummyFileClass(File fileOne)
{
this.fileOne = fileOne;
fileTwo = findRelatedFile(fileOne)
}
public void someMethod()
{
// Do something with files one and two
}
}
In my unit test, I want to be able to to test someMethod() without having to have physical files sitting somewhere. I can mock fileOne, and pass it to the constructor, but since fileTwo is being calculated in the constructor, I don't have control of this.
I could mock the method findRelatedFile() - but is this the best practice? Looking for the best design rather than a pragmatic workaround here. I'm fairly new to mocking frameworks.

In this sort of situation, I would use physical files for testing the component and not rely on a mocking framework. As fge mentions it may be easier plus you don't have to worry about any incorrect assumptions you may make of your mock.
For instance, if you rely upon File#listFiles() you may have your mock return a fixed list of Files, however, the order they are returned in is not guaranteed - a fact you may only discover when you run your code on a different platform.
I would consider using JUnit's TemporaryFolder rule to help you set up the file and directory structure you need for your test, e.g.:
public class DummyFileClassTest {
#Rule
public TemporaryFolder folder = new TemporaryFolder();
#Test
public void someMethod() {
// given
final File file1 = folder.newFile("myfile1.txt");
final File file2 = folder.newFile("myfile2.txt");
... etc...
}
}
The rule should clean up any created files and directories when the test completes.

Related

How to change values of "private static final String filePath" for unittesting

I have my class as
public class MappingLoader
{
private static final String filepath = "/tmp/mapping.properties" // unix path of production system
private static Map<String,String> mapping = new HashMap<String,String>()
static
{
loadMappingFile()
}
#VisibleForTesting
static void loadMappingFile()
{
//reading properties files here
final Properties prop = new Properties();
try (final InputStream input = Files.newInputStream(Paths.get(filepath)))
{
prop.load(input);
}
catch (final Exception e)
{
...
...
throw new RuntimeException(e);
}
//now load "mapping" from properties file
....
....
}
}
For testing, I need to change the value of string variable "filepath" such that it should take development system path(say c:\project\target\mapping.properties)
I have tried powermocks in junits, but it always throws exception and terminates.
annotations at class level:
#RunWith(PowerMockRunner.class)
#SuppressStaticInitializationFor("some.package.ClassWithStaticInit")
and in test case:
Whitebox.setInternalState(Mappingloader.class, "filepath", testfilepath);
Mappingloader.loadMappingFile();
I also tried to change this via reflection as given in(Change private static final field using Java reflection) but it always throws the FileNotFoundException for "filepath" and does not takes the changed path "testFilePath"
Is there any way i can change this variable such that it does not throw FileNotFoundException without any modification in source code?
If I remove "throw new RuntimeException(e);" in source code, powermocks works for me. but i want to achieve this without modifying source code, either via powermock, reflection api.
Well, you can try your luck with Powermock; and that should work (maybe if you spend some more hours reading its documentation and making experiments); but honestly: your problem is not testing. Your problem is that you created untestable code. And now you are trying to use the big powermock hammer to "fix" what is your broken design.
You see, when using static methods and constants; people think they "save" on performance (which is true; but to a very small degree; which probably doesn't matter for 99.999% of all applications); but they keep forgetting that using static leads to direct coupling of different functionalities. static is an abnormality in good OO design; and should be used with great care.
So, in your case, you could replace the whole thing with something along these lines:
interface MappingLoader {
Map<String, String> loadMappingsFrom(String fileName);
}
class MappingLoaderImpl implements MappingLoader {
...
and you see, all of a sudden you are only dealing with "real" interfaces and classes; and non-static methods; and surprise: now you can fully unit-test the whole thing; and most likely, you don't even need a mocking framework. You just create a temp file with mappings somewhere; and then you make sure that your impl class gives you the mappings from that file.
Zero mocking; zero testing of internal implementation details; just a few asserts.
And another advantage on top: all your client code that should only be using the MappingLoader interface can also be tested. Because ordinary frameworks like EasyMock or Mockito will allow you to mock out instances of that interface ... because: no static calls any more!
That is how you change the value of private final static fields - by not using them!
(and if I made you curious: watch this to learn how to write testable code from the beginning)
While I completely agree with #GhostCat's response, I understand you are looking for a solution not involving to change the source code. Have you thought of changing the contents of /tmp/mapping.properties before the test runs (and restore them later)?
static final String fields or any final static primitives fields cannot be modified in runtime. If speed honestly you can modify these fields, but your change will not affect code which uses these fields, because during compile time reference is replaced by value.
My suggestion: use static mock to Files.newInputStream()call and then return ByteArrayInputStream with expected data. In this case you will avoid fragile disk IO operation which could affect your test stability.

How can I write valid JUnit tests when mocking a filepath?

I am trying to write some JUnit tests for a set of methods which use some REST services on the web.
In general, within my methods, I am providing a filepath and a configuration as a parameter, but I expect things will get more complicated as I progress.
For right now, what are the best ways for me to write JUnit tests for the likes of :
public Answers changeFileToAnswer(String filePath, String mediaType) {
File document = new File(filePath);
Answers answers = restService.changeFileToAnswer(document, mediaType);
return answers;
}
What kind of Unit tests can I write for a simple class like this? Testing the answers object would be an integration tests, since an external call is made here, right? What is good practise here? Is there a way to mock the filepath being passed in as a parameter?
Notes -
This method is from a REST interface which will later be exposed through a GUI. I am currently testing it with POST calls from POSTman. Due to this, I am passing in a string for the filePath rather than a file object (as I could not post this to my server).
Thanks.
The test is not necessary to be integration. Your restService need to be mock or fake, so there is no real external call.
For mocking filePath you can use JUnit TemporaryFolder.
public class TestClass{
#Rule
private TemporaryFolder folder = new TemporaryFolder();
#Test
public void testMethod(){
File tempFile = folder.newFile("myfile.txt");
classUnderTest.changeFileToAnswer(file.getPath(), mediaType);
}
}
This rule will create a real file in file system which will be removed when tests finish execution.
UPD: You might also want to take a look at jimfs

How can a unit test "test the contract" on a method that returns void?

Java 8 here but this is a general unit testing question that (is likely) language-agnostic.
The syntax of writing a JUnit test is easy, but deciding on what tests to write and how to test main/production code is what I find to be the biggest challenge. In reading up on unit testing best practices, I keep hearing the same thing over and over again:
Test the contract
I believe the idea there is that unit tests should not be brittle and should not necessarily break if the method's implementation changes. That the method should define a contract of inputs -> results/outcomes and that the tests should aim to verify that contract is being honored. I think.
Let's say I have the following method:
public void doFizzOnBuzz(Buzz buzz, boolean isFoobaz) {
// wsClient is a REST client for a microservice
Widget widget = wsClient.getWidgetByBuzzId(buzz.getId());
if(widget.needsFile()) {
File file = readFileFromFileSystem(buzz.getFile());
if(isFoobaz) {
// Do something with the file (doesn't matter what)
}
}
return;
}
private File readFileFromFileSystem(String filename) {
// Private helper method; implementation doesn't matter here EXCEPT...
// Any checked exceptions that Java might throw (as a result of working)
// with the file system are wrapped in a RuntimeException (hence are now
// unchecked.
// Reads a file from the file system based on the filename/URI you specify
}
So here, we have a method we wish to write unit tests for (doFizzOnBuzz). This method:
Has two parameters, buzz and isFoobaz
Uses a class property wsClient to make a network/REST call
Calls a private helper method that not only works with the external file system, but that "swallows" checked exceptions; hence readFileFromFileSystem could throw RuntimeExceptions
What kinds of unit tests can we write for this that "test the contract"?
Validating inputs (buzz and isFoobaz) are obvious ones; the contract should define what valid values/states for each of those are, and what exceptions/results should occur if they are invalid.
But beyond that, I'm not really sure what the "contract" here would even be, which makes writing tests for it very difficult. So I guess this question really should be something like "How do I determine what the contract is for a unit test, and then how do you write tests that target the contract and not the implementation?"
But that title would be too long for a SO question.
Your code with the methods doFizzOnBuzz(Buzz buzz, boolean isFoobaz) and private File readFileFromFileSystem(String filename) is not easily testable, because the first method will try and read a file, and that's not something you want to do in test.
Here, doFizzOnBuzz needs something to provide a File for it to work with. This FileProvider (as I'll call it) could be an interface, something like:
public interface FileProvider {
File getFile(String filename);
}
When running in production, an implementation to actually read the file from disk is used, but when unit testing doFizzOnBuzz a mock implementation of FileProvider could be used instead. This returns a mock File.
The key point to remember is that when testing doFizzOnBuzz, we are not testing whatever provides the file, or anything else. We assume that to working correctly. These other bits of code have their own unit tests.
A mocking framework such as Mockito can be used a create mock implementations of FileProvider and File, and to inject the mock FileProvider into the class under test, probably using a setter:
public void setFileProvider(FileProvider f) {
this.fileProvider = f;
}
Also, I don't know what a wsClient is, bit I do know it has a getWidgetByBuzzId() method. This class too could be an interface, and for testing purposes the interface would be mocked, and return a mock Widget, similar to the FileProvider above.
With mockito, not only can you set up mock implementations of interfaces, you can also define what values are returned when methods are called on that interface: e.g.
//setup mock FileProvider
FileProvider fp = Mockito.mock(FileProvider.class);
//Setup mock File for FileProvider to return
File mockFile = Mockito.mock(File.class);
Mockito.when(mockFile.getName()).thenReturn("mockfilename");
//other methods...
//Make mock FileProvider return mock File
Mockito.when(fp.getFile("filename")).thenReturn(mockFile);
ClassUnderTest test = new ClassUnderTest();
test.setFileProvider(fp); //inject mock file provider
//Also set up mocks for Buzz,, Widget, and anything else
//run test
test.doFizzOnBuzz(...)
//verify that FileProvider.getFile() was actually called:
Mockito.verify(fp).getFile("filenane");
The above test fails if getFile() was not called with the parameter 'filename'
Conclusion
If you cannot directly observe the results of a method, e.g. it is void, you can use Mocking to verify its interaction with other classes and methods.
The problem is that your contract method does not tell what effect you can observe from the outside. It is basically a BiConsumer, so appart from ensuring there is an exception or not, there is not much unit testing possible.
The test you could do is to ensure that the (Mocked) REST service is called, or that the File (part of the Buzz parameter, which might be pointing to a temporary file) will be impacted by the method under some conditions.
If you want to unit test the output of the method, you may need to refactor to separate the determination of what should be done (file needs update) from actually doing it.

Unit testing file write in Java without interface wrappers

I have an existing Java class that writes to a file.
public final class WriteToFile{
private Writer file_writer;
private static final String encoding_format = "UTF8";
private FileWrite(final File fpath) throws IOException {
this.file_writer = new OutputStreamWriter(new FileOutputStream(fpath), encoding_format);
}
#Override
public void fileWrite(final String msg) {
try {
this.file_writer.write(msg);
this.file_writer.write("\n");
this.file_writer.flush();
this.file_writer.close();
} catch (IOException e) {
log.error("File write failed", e);
}
}
}
In order to unit test this, I learnt that creating a file mock using a Mocking framework is not a good practice . What do I test here? The only way of testing this is to probably do the file write again, and check if the expected contents and actual contents are the same. In that case, doing it the JUnit way would be as mentioned in this post How to test write to file in Java?. However, I am not going to rewrite the file writing code, to include interface wrappers. How do I go about with this?
#Test public void testfileWrite() {
String msg = "somemessage";
String fpath = "path/to/file";
Writer file_writer = new OutputStreamWriter(new FileOutputStream(fpath), "UTF8");
file_writer.write(msg);
assertEquals("somemessage", file_writer.toString());
}
Is this all that needs to be tested?
The point of this class is to write a file. It does nothing else (and that is a good thing). So don't bother with a mockist unit test, all it shows is that you can write a ton of mock code. Instead write a Integration Test.
Use the JUnit rule TemporaryFolder to create and destroy a folder to put your test file in, then verify the file has what you want in it at the end of the test. The only time you should consider mocking for this kind of test is if the exceptional case does something funky. Then you can either do some evil black magic involving Powermock or pass in some form of "File Stream factory". Or ask yourself if that is really such a great place for complex logic that needs testing, and then move it.
When testing classes that make use of WriteToFile, mock or stub WriteToFile.
When it comes to write unit tests that must check generated files, I always prepare myself a repository of cases: For each case, an input file (if necessary), and and a set of expected output files.
I write one test method for each case, where I call the business logic, which will generate one (or some) file into the working directory, and I eventually check if the generated file is equal to the proper expected file.
I prepare the expected files manually and check them in into the Source Control System, so that they belong to each released version. If, in future, the business logic must change its behaviour, then it is required that the expected file be changed accordingly, and that both the code and the file be checked in and tagged together in the same release.
That is the easiest and safest way I found for checking generated files.
Use Powermockito to mock the call to the constructor of FileOutputStream and OutputStreamWriter as in: http://benkiefer.com/blog/2013/04/23/powermockito-constructor-mocking/
Then verify that file_writer methods write(String) and flush() have been invoked twice and once, respectively; and at the end close().

Junit Testing Mocking a File Operation

I have a piece of code similar to the below that I have been asked to Junit test. We are using Junit, EasyMock and Spring Framework. I've not done much Junit testing, and am a bit lost as to how I can mock the below.
Basically the path to the file as in the directory it will be in, won't exist when I'm writing or running the test on my machine. I'm wondering is there a way to mock the object to a temporary location as the location it will actually run with is guaranteed to exist and be tested during integration testing.
However I'm wondering would it even be wise to do so or should this be tested when it is integrated with the rest of the project when the directory will actually exist.
Any help appreciated, as Junit testing is completely new to me. I've looked around but can't see how to do what I want (might be a good hint that I shouldn't be doing it :P).
String fileName = pathToFileName;
File file = new File(fileName);
if (file.exists()) {
FileUtil.removeLineFromFile(file, getValueToRemove(serialNumber));
}
First option is to inject the file into your class where you can just inject the mock directly. Usually the better option, but not always elegant or feasible.
I've gotten some mileage out of these things by creating a protected wrapper function for problematic objects such as this. In your class under test:
protected File OpenFile(String fileName) { return new File(filename;}
In the test class:
File file = EasyMock.createNiceMock(File.class);
private MyClass createMyClass() {
return new MyClass() {
#Override protected File OpenFile(String fileName) { return file; }
};
}
#Test public testFoo() {
EasyMock.expect(file.exists()).andStubReturn(true);
//...
MyClass myClass=createMyClass();
// ...
}
If you need, you can save off the construction parameters (fileName) in this case for validation.

Categories

Resources