How do you mock file reading/writing via JUnit?
Here is my scenario
MyHandler.java
public abstract class MyHandler {
private String path = //..path/to/file/here
public synchronized void writeToFile(String infoText) {
// Some processing
// Writing to File Here
File file = FileUtils.getFile(filepath);
file.createNewFile();
// file can't be written, throw FileWriteException
if (file.canWrite()) {
FileUtils.writeByteArrayToFile(file, infoText.getBytes(Charsets.UTF_8));
} else {
throw new FileWriteException();
}
}
public String readFromFile() {
// Reading from File here
String infoText = "";
File file = new File(path);
// file can't be read, throw FileReadException
if (file.canRead()) {
infoText = FileUtils.readFileToString(file, Charsets.UTF_8);
} else {
throw FileReadException();
}
return infoText
}
}
MyHandlerTest.java
#RunWith(PowerMockRunner.class)
#PrepareForTest({
MyHandler.class
})
public class MyHandlerTest {
private static MyHandler handler = null;
// Some Initialization for JUnit (i.e #Before, #BeforeClass, #After, etc)
#Test(expected = FileWriteException.class)
public void writeFileTest() throws Exception {
handler.writeToFile("Test Write!");
}
#Test(expected = FileReadException.class)
public void readFileTest() throws Exception {
handler.readFromFile();
}
}
Given above source, Scenario when file is not writable (write permission not allowed) is OK, However, when i try to do scenario wherein file is not readable (read permission not allowed). It always read the file, i have already tried to modify the file permission on the test code via below
File f = new File("..path/to/file/here");
f.setReadable(false);
However, I did some reading, setReadable() always returns false (failed) when run on Windows machine.
Is there a way to modify the file permission of the target file programmatically in relation to JUnit?
Note
Target source code to test cannot be modified, meaning
Myhandler.class is a legacy code which is not to be modified.
Instead of relying on the operating system file permissions, use PowerMock to mock FileUtils.getFile(...) and make it return an instance of File (e.g. anonymous sub class) that returns a specific value for canWrite()/canRead().
Mocking static methods with Mockito
Since Mockito cannot mock static methods, use a File factory instead (or refactor your FileUtils to be a factory), then you can mock it and return a mocked File instance as well, where you can also mock any File methods you want.
So instead of FileUtils.getFile(filepath) you will now have something like FileFactory.getInstance().getFile(filepath) for example, where you can mock getFile(String) method easily.
In jUnit there's a handy rule for scenarios like yours.
public class MyHandlerTest {
#Rule
// creates a temp folder that will be removed after each test
public org.junit.rules.TemporaryFolder folder = new org.junit.rules.TemporaryFolder();
private MyHandler handler;
#Before
public void setUp() throws Exception {
File file = folder.newFile("myFile.txt");
// do whatever you need with it - fill with test content and so on.
handler = new MyHandler(file.getAbsolutePath()); // use the real thing
}
// Test whatever behaviour you need with a real file and predefined dataset.
}
Related
/*
this method will create the required manifest file in compatible format such that
quicksight can import data from specified s3 bucket
*/
private JSONObject CreateManifestFileJSONObject(JSONObject ManifestFile){
JSONArray URIPrefixArray= new JSONArray();
URIPrefixArray.put(PrefixLocation);
JSONObject URIPrefixJSONObject= new JSONObject();
URIPrefixJSONObject.put("URIPrefixes",URIPrefixArray);
JSONArray FileLocationsArray= new JSONArray();
FileLocationsArray.put(URIPrefixJSONObject);
JSONObject globalUploadSettings= new JSONObject();
globalUploadSettings.put("format","JSON");
ManifestFile.put("globalUploadSettings",globalUploadSettings);
ManifestFile.put("fileLocations",FileLocationsArray);
return(ManifestFile);
}
/*
this method will upload the ManifestFile to same S3 Bucket in which data files is stored
*/
private void UploadManifestFileJSONObjectToS3(JSONObject ManifestFile){
try {
AmazonS3 S3Client = new Utility().SetUpS3Client();
byte[] fileContentBytes = (ManifestFile.toString()).getBytes();
InputStream fileInputStream = new ByteArrayInputStream(fileContentBytes);
ObjectMetadata objectMetadata = new ObjectMetadata();
objectMetadata.setContentLength(fileContentBytes.length);
S3Client.putObject(new PutObjectRequest(Bucket_Name, ManifestFileName, fileInputStream, objectMetadata).withCannedAcl(CannedAccessControlList.PublicRead));
}
catch(Exception exception){
exception.printStackTrace();
}
}
public void handler() {
System.out.println("inside the manifest file");
try {
JSONObject ManifestFile = new JSONObject();
ManifestFile = CreateManifestFileJSONObject(ManifestFile);
UploadManifestFileJSONObjectToS3(ManifestFile);
}
catch(Exception exception){
exception.printStackTrace();
}
I want to test handler method but handlor method is calling private methods so I do not know how to write the test class for this code.
i want to write unit test for this class please help
this is the test class I am able to create up to this point but it will not surely mock s3 behaviour
#Test
public void handler() {
ManifestFileHandler manifestFileHandler=new ManifestFileHandler();
manifestFileHandler.handler();
}
You can test private methods with the help of PowerMock provides utilities which uses reflection to do certain things.
check the below example,
https://examples.javacodegeeks.com/core-java/mockito/mockito-mock-private-method-example-with-powermock/
There are two schools of thought on unit testing private functions. The first is that you make them public (or protected or package accessible) and test them as you would a public function. The second is that if they are private they are part of the encapsulated implementation detail and you only need to test them through the public functions.
My personal view is that complicated private functions are often a sign that you are breaking the single responsibility principle and it's likely you should have logic in private functions that should be split into a separate class that can then be tested through its public methods.
With respect to the code you've posted you have a larger problem than how to test the private functions: your class depends on other classes that you don't have control over. You have no way of mocking the behaviour of those classes to test various scenarios or to verify that they have been called correctly. I suspect it is this problem that is really behind your question.
As an example, I would suggest you inject a S3Client into your class rather than create it internally through new Utility().SetUpS3Client(). That way you can mock its behaviour and verify it is called correctly by your code. Attempting to do that with the real version of this class will be challenging.
So using this model, your code might look something like:
public class ManifestFileHandler {
private final S3Client client;
public ManifestFileHandler(S3Client client) {
this.client = client;
}
private void upload(JSONObject manifestFile) {
...
client.putObject(...);
}
public void handleManifest() {
...
upload(manifestFile);
...
}
}
And your test code (using mockito):
#Test
void testManifestUpload() {
S3Client client = mock(S3Client.class);
ManifestFileHandler handler = new ManifestFileHandler(client);
handler.handleManifest();
verify(client).putObject(expectedObject);
}
If you need to capture the argument passed to putObject and assert various aspects of it then that is possible with most mocking tools (including mockito) but is beyond the scope of your question.
I'm trying to rename an existing file using File#renameTo(), but it doesn't seem to work.
The following code represents what I am trying to do:
public class RenameFileDirectory {
public static void main(String[] args) throws IOException {
new RenameFileDirectory();
}
public RenameFileDirectory() throws IOException {
File file = new File("C:\\Users\\User-PC\\Desktop\\Nouveau dossier2\\file.png");
File desFile = new File ("C:\\Users\\User-PC\\Desktop\\Nouveau dossier2\\file2.png");
if (file.renameTo(desFile)) {
System.out.println("successful rename");
} else {
System.out.println("error");
}
}
}
Try using Files.move instead. If you read the javadocs for renameTo, it states that:
Many aspects of the behavior of this method are inherently platform-dependent: The rename operation might not be able to move a file from one filesystem to another, it might not be atomic, and it might not succeed if a file with the destination abstract pathname already exists. The return value should always be checked to make sure that the rename operation was successful.
I wanted to test the IOException and IllegalArgumentException thrown by properties.load(in) method. As per the documentation here OracleDoc it says the load method throws IOException - if an error occurred when reading from the input stream. IllegalArgumentException - if the input stream contains a malformed Unicode escape sequence.
Here is my code:
public class PropertiesRetriever {
private String foo;
private String foo1;
private Properties properties;
/**
* Injects the properties file Path in the {GuiceModule}
* Calls {#link PropertiesRetriever#loadPropertiesPath(String) to load the
* properties file.
*/
#Inject
public PropertiesRetriever(#Named("propertiesPath") String propertiesPath, Properties properties)
throws IOException {
this.properties = properties;
loadPropertiesPath(propertiesPath);
}
/**
* Loads the properties file as inputstream.
*
*/
public void loadPropertiesPath(String path) throws IOException {
InputStream in = this.getClass().getResourceAsStream(path);
properties.load(in);
}
Here, a method:
properties.load(in)
throws IOException and IllegalArgumentException. I wanted to test this methods in JUnit testing. Is there anyway I can call these methods.
You can do it by refactoring your code a little. That and use Mockito or some other mocking framework to create an InputStream that behaves as you desire (throw exceptions):
public void loadPropertiesPath(String path) throws IOException {
// Always close streams you open!
try (InputStream in = getIStream(path)) {
properties.load(in);
}
}
private InputStream getIStream(String path) {
InputStream in = this.getClass().getResourceAsStream(path);
return in;
}
You can use mockito to create a partial mock of your object; mock getIStream(String) to return a mock InputStream. Set up the mock to throw the exception you want when InputStream::read(byte[]) gets called.
If you do not want to use PowerMock then you can change the visibility of getIStream(String) to default. Then plain mockito will do the job:
#Test
public void exceptionTest() throws IOException {
PropertiesRetriever pr = new PropertiesRetriever();
PropertiesRetriever prSpy = spy(pr);
InputStream isMock = mock(InputStream.class);
doReturn(isMock).when(prSpy).getIStream(anyString());
doThrow(new IllegalArgumentException("CRASH!")).when(isMock).read(any());
prSpy.loadPropertiesPath("blah");
}
You have two choices. Either provide some test files, that will create expected errors, or pass mock of Stream to Properties retriever as parameter. So instead of propertiesPath parameter, you will have directly inputStream (this approach may just move your problem somewhere else).
If you decide to pass Stream as a parameter, there are some tips, how to mock it: Mocking Java InputStream
I'm trying to write a unit test that checks if methods were invoked in an order. To do that I'm using Mockito's inOrder.verify() like this:
#Test
public void shouldExecuteAllFileCommandsOnAFileInFIFOOrder() {
// Given
ProcessFileListCommand command = new ProcessFileListCommand();
FileCommand fileCommand1 = mock(FileCommand.class, "fileCommand1");
command.addCommand(fileCommand1);
FileCommand fileCommand2 = mock(FileCommand.class, "fileCommand2");
command.addCommand(fileCommand2);
File file = mock(File.class, "file");
File[] fileArray = new File[] { file };
// When
command.executeOn(fileArray);
// Then
InOrder inOrder = Mockito.inOrder(fileCommand1, fileCommand2);
inOrder.verify(fileCommand1).executeOn(file);
inOrder.verify(fileCommand2).executeOn(file);
}
However, the second verify() fails with the following error:
org.mockito.exceptions.verification.VerificationInOrderFailure:
Verification in order failure
Wanted but not invoked:
fileCommand2.executeOn(file);
-> at (...)
Wanted anywhere AFTER following interaction:
fileCommand1.executeOn(file);
-> at (...)
If I change .executeOn(file) to .executeOn(any(File.class)) the test passes, but I want to make sure that the methods are invoked using the same argument.
Here's the class I'm testing:
public class ProcessFileListCommand implements FileListCommand {
private List<FileCommand> commands = new ArrayList<FileCommand>();
public void addCommand(final FileCommand command) {
this.commands.add(command);
}
#Override
public void executeOn(final File[] files) {
for (File file : files) {
for (FileCommand command : commands) {
file = command.executeOn(file);
}
}
}
}
The test fails because the argument to the second executeOn() method call is not the same file as the argument of the first one, since the first file is replaced by another one in
file = command.executeOn(file);
I wrote unit test (JUnit 4) that performs some logic and writes result to file. In #Before annotated method it creates file and in #After the file should be deleted. It isn't though, and I can't figure it out, why.
I am using Google Guava 10.01 Files API. Here's my unit test code:
public class CashierTest extends ContextedTest {
private File cashierFile;
#Before
public void createFile() throws Exception {
cashierFile = new File("D://workspace-sts/spring-miso/cashier.txt");
cashierFile.createNewFile();
}
#After
public void release() {
if (cashierFile.exists()) {
if (!cashierFile.delete()) {
System.out.println("Couldn't delete cashier file");
}
}
cashierFile = null;
}
#Test
public void testCashier() throws Exception {
// file shouldn't contain any text
assertFalse(Files.toString(cashierFile, Charset.defaultCharset()).length() > 0);
Cashier cashier = (Cashier) context.getBean("cashier");
ShoppingCart cart = (ShoppingCart) context.getBean("shoppingCartPrototype");
cashier.checkout(cart);
assertTrue(cashierFile.exists());
// file should contain text now
assertTrue(Files.toString(cashierFile, Charset.defaultCharset()).length() > 0);
}
#Override
protected void setPath() {
path = "sk/xorty/advancedioc/beans.xml";
}
}
Note: ContextedTest superclass is my test which holds Spring container it isn't relevant atm.
Simply instanting a File does not mean that an actual file will be created. Call createNewFile() or createTempFile() on that instance for this.
Within your test method you don't seem to pass that file reference to anyone that could possibly create the file or write anything in it... Am I missing something or is the code you posted missing some key lines ?
You should use the TemporaryFolder Rule with JUnit 4. This will handle the setup and teardown of temporary test directories and files.
public static class HasTempFolder {
#Rule public TemporaryFolder folder= new TemporaryFolder();
#Test public void testUsingTempFolder() throws IOException {
File createdFile= folder.newFile("myfile.txt");
...
}
}
Other Rules are part also part of Junit 4.