I'm creating a TemporaryFolder using the #Rule annotation in JUnit 4.7. I've tried to create a new folder that is a child of the temp folder using tempFolder.newFolder("someFolder") in the #Before (setup) method of my test. It seems as though the temporary folder gets initialized after the setup method runs, meaning I can't use the temporary folder in the setup method. Is this correct (and predictable) behavior?
This is a problem in Junit 4.7. If you upgrade a newer Junit (for example 4.8.1) all #Rule will have been run when you enter the #Before method:s. A related bug report is this: https://github.com/junit-team/junit4/issues/79
This works as well. EDIT if in the #Before method it looks like myfolder.create() needs called. And this is probably bad practice since the javadoc says not to call TemporaryFolder.create(). 2nd Edit Looks like you have to call the method to create the temp directories if you don't want them in the #Test methods. Also make sure you close any files you open in the temp directory or they won't be automatically deleted.
<imports excluded>
public class MyTest {
#Rule
public TemporaryFolder myfolder = new TemporaryFolder();
private File otherFolder;
private File normalFolder;
private File file;
public void createDirs() throws Exception {
File tempFolder = myfolder.newFolder("folder");
File normalFolder = new File(tempFolder, "normal");
normalFolder.mkdir();
File file = new File(normalFolder, "file.txt");
PrintWriter out = new PrintWriter(file);
out.println("hello world");
out.flush();
out.close();
}
#Test
public void testSomething() {
createDirs();
....
}
}
Related
I am trying to write a JUnit test for a java code that with three methods.
FileDeletion.java:
public static void fileDeletion(String filePath) {
File file = new File(filePath);
file.delete();
}
I looked online on how to test this, but it came up with how to make a temporary file in JUnit which is "guaranteed to be deleted after the test finishes".
See: https://howtodoinjava.com/junit/junit-creating-temporary-filefolder-using-temporaryfolder-rule/
How do I make a file which can be created and then subsequently deleted in a JUnit Test?
Any help would go a long way, many thanks.
I would consider using JUnit's TemporaryFolder rule to help you set up the file and directory structure you need for your test which is cleaned up after your test.
public class DeleteFileTest {
#Rule
public TemporaryFolder folder = new TemporaryFolder();
#Test
public void test() {
final File file = folder.newFile("myfile1.txt");
file.delete();
//Assert
}
}
I'm using a temporary folder in my unit test using the #Rule of JUnit.
The folder is not being deleted after the tests finish
Java version : 1.8
JUnit version: 4.12
Os windows 10
I'm creating a file.csv under a temp folder in order to edit it using CSVPrinter from org.apache.commons
public class MyService {
public void createCSVFile(String basePath) {
try (FileWriter out = new FileWriter(format("%s/file.csv", basePath));
CSVPrinter printer = new CSVPrinter(out, CSVFormat.DEFAULT.withHeader(HEADERS))) {
//printer.PrintRecord(args);
} catch (IOException e) {
log.error("Failed to create file.csv under " + basePath, e);
}
}
}
The test:
#Rule
public TemporaryFolder folder = new TemporaryFolder();
private String folderPath;
private MyService myService;
#Before
public void setup() throws IOException {
folder.newFile("file.csv");
folderPath = folder.getRoot().getPath();
}
#Test
public void testing_myService() {
myService.createCSVFile(folderPath);
//Assert
}
When i open the folder ex: C:\Users\me\AppData\Local\Temp\junit2290989758736528709
I can still see the file.csv as it was not deleted
I tried to migrate the create of the file to become in the tests. It did not fix it
#Test
public void testing_myService() {
folder.newFile("file.csv");
myService.createCSVFile(folder.getRoot().getPath());
//Assert
}
I added an #After method to delete the folder
#After
public void cleanup() throws IOException {
//Tried several ways to delete the folder and file as well
//FileUtils.forceDelete(folder.getRoot().getAbsoluteFile());
//folder.getRoot().delete();
//FileUtils.deleteDirectory(folder.getRoot());
//new File(folderPath +"/file.csv").delete();
//All methods did not delete neither the direcotry (with exception can't delete file.csv) nor deleted the file.csv
}
I also tried to call the folder.create(); and folder.delete(); myself without using the #Rule
But the folder wasn't deleted as well
The file.csv and folder are marked as read-only when viewing the properties on windows
I tried to change that in the code
folder.setWritable(true);
folder.setReadable(true);
folder.setExecutable(true);
Didn't manage to delete the folder as well
My aim is to find a solution that works for any environment where the code is checked out ex: CI pipeline on a linux server and on windows
any other reason for this behavior?
I suspect that some code is holding a file handle, preventing the temporary directory from being deleted.
I suggest:
#Rule
public final TemporaryFolder folder = TemporaryFolder.builder()
.assureDeletion()
.build();
That should cause TemporaryFolder to throw an exception if it could not delete the folder.
You should probably be creating and deleting in a folder within your project structure.
But it's probably not working because of a permission issue:
Either change the permissions of the folder so that the program has priveledges to delete, or run the program/IDE as an administrator.
That should fix the problem.
I'm writing a UDF for Pig using Java. It works fine but Pig doesn't give me options to separate environment. What my Pig script is doing is to get Geo location from IP address.
Here's my code on the Geo location part.
private static final String GEO_DB = "GeoLite2-City.mmdb";
private static final String GEO_FILE = "/geo/" + GEO_DB;
public Map<String, Object> geoData(String ipStr) {
Map<String, Object> geoMap = new HashMap<String, Object>();
DatabaseReader reader = new DatabaseReader.Builder(new File(GEO_DB)).build();
// other stuff
}
GeoLite2-City.mmdb exists in HDFS that's why I can refer from absolute path using /geo/GeoLite2-City.mmdb.
However, I can't do that from my JUnit test or I have to create /geo/GeoLite2-City.mmdb on my local machine and Jenkins which is not ideal. I'm trying to figure out a way to make my test passed while using new File(GEO_DB) and not
getClass().getResourceAsStream('./geo/GeoLite2-City.mmdb') because
getClass().getResourceAsStream('./geo/GeoLite2-City.mmdb')
Doesn't work in Hadoop.
And if I run Junit test it would fail because I don't have /geo/GeoLite2-City.mmdb on my local machine.
Is there anyway I can overcome this? I just want my tests to pass without changing the code to be using getClass().getResourceAsStream and I can't if/else around that because Pig doesn't give me a way to pass in parameter or maybe I'm missing something.
And this is my JUnit test
#Test
#Ignore
public void shouldGetGeoData() throws Exception {
String ipTest = "128.101.101.101";
Map<String, Object> geoJson = new LogLine2Json().geoData(ipTest);
assertThat(geoJson.get("lLa").toString(), is(equalTo("44.9759")));
assertThat(geoJson.get("lLo").toString(), is(equalTo("-93.2166")));
}
which it works if I read the database file from resource folder. That's why I have #Ignore
Besides, your whole code looks pretty un-testable.
Every time when you directly call new in your production code, you prevent dependency injection; and thereby you make it much harder to test your code.
The point is to not call new File() within your production code.
Instead, you could use a factory that gives you a "ready to use" DatabaseReader object. Then you can test your factory to do the right thing; and you can mock that factory when testing this code (to return a mocked database reader).
So, that one file instance is just the top of your "testing problems" here.
Honestly: don't write production code first. Do TDD: write test cases first; and you will quickly learn that such production code that you are presenting here is really hard to test. And when you apply TDD, you start from "test perspective", and you will create production code that is really testable.
You have to make the file location configurable. E.g. inject it via constructor. E.g. you could create a non-default constructor for testing only.
public class LogLine2Json {
private static final String DEFAULT_GEO_DB = "GeoLite2-City.mmdb";
private static final String DEFAULT_GEO_FILE = "/geo/" + GEO_DB;
private final String geoFile;
public LogLine2Json() {
this(DEFAULT_GEO_FILE);
}
LogLine2Json(String geoFile) {
this.geoFile = geoFile;
}
public Map<String, Object> geoData(String ipStr) {
Map<String, Object> geoMap = new HashMap<String, Object>();
File file = new File(geoFile);
DatabaseReader reader = new DatabaseReader.Builder(file).build();
// other stuff
}
}
Now you can create a file from the resource and use this file in your test.
public class LogLine2JsonTest {
#Rule
public final TemporaryFolder folder = new TemporaryFolder();
#Test
public void shouldGetGeoData() throws Exception {
File dbFile = copyResourceToFile("/geo/GeoLite2-City.mmdb");
String ipTest = "128.101.101.101";
LogLine2Json logLine2Json = new LogLine2Json(dbFile.getAbsolutePath())
Map<String, Object> geoJson = logLine2Json.geoData(ipTest);
assertThat(geoJson.get("lLa").toString(), is(equalTo("44.9759")));
assertThat(geoJson.get("lLo").toString(), is(equalTo("-93.2166")));
}
private File copyResourceToFile(String name) throws IOException {
InputStream resource = getClass().getResourceAsStream(name);
File file = folder.newFile();
Files.copy(resource, file.toPath(), StandardCopyOption.REPLACE_EXISTING);
return file;
}
}
TemporaryFolder is a JUnit rule that deletes every file that is created during test afterwards.
You may modify the asserts by using the hasToString matcher. This will give you more detailed information in case of a failing test. (And you have to read/write less code.)
assertThat(geoJson.get("lLa"), hasToString("44.9759"));
assertThat(geoJson.get("lLo"), hasToString("-93.2166"));
You don't. Your question embodies a contradiction in terms. Resources are not files and do not live in the file system. You can either distribute the file separately from the JAR and use it as a File or include it in the JAR and use it as a resource. Not both. You have to make up your mind.
I am trying to test a method that copies a source file to a dest file using JUnit's TemporaryFolder. I get a Java IOException when I try run this test however. Does it matter where I make the declaration for the folder? (My test class has several different tests in it). And if so, what is the proper way to do it? I ask because I currently have several unit tests above this code, then I try to set up the testing for the file copying. Maybe the #Rule-#Before-#Test block needs to be in its own class? Here is the snippet where I have coded the test:
...other tests...then:
#Rule
public static TemporaryFolder tmp = new TemporaryFolder();
private File f1, f2;
#Before
public void createTestData() throws IOException {
f1 = tmp.newFile("src.txt");
f2 = tmp.newFile("dest.txt");
BufferedWriter out = new BufferedWriter(new FileWriter(f1));
out.write("This should generate some \n" +
"test data that will be used in \n" +
"the following method.");
out.close();
}
#Test
public void copyFileTest() {
out.println("file 1 length: " + f1.length());
try {
copyFile(f1, f2);
} catch (IOException e) {
e.getMessage();
e.printStackTrace();
}
if (f1.length() != f2.length())
fail();
else if (!f1.equals(f2))
fail();
assertSame(f1, f2);
}
When I run this test class, all 11 of my tests now fail (which previously passed) and I get java.io.IOException: No such file or directory.
So looking at the JUnit Javadoc, I have found out that any declaration under #Rule must be public, and not static. So I took out the static and just have:
#Rule
public TemporaryFolder tmp = new TemporaryFolder();
I still do not know for sure if it matters where this declaration is made when you have other unit tests in your class that do not use the #Rule declaration, but this did allow me to run through my tests successfully.
If you really want to declare TemporaryFolder as static, you can use #ClassRule which is used to annotate static fields that contains Rule.
#ClassRule
public static TemporaryFolder tmp = new TemporaryFolder();
Reference: http://junit-team.github.io/junit/javadoc/4.10/org/junit/ClassRule.html
I'm writing a test for a method that creates a file in a directory. Here's what my JUnit test looks like:
#Before
public void setUp(){
objectUnderTest = new ClassUnderTest();
//assign another directory path for testing using powermock
WhiteBox.setInternalState(objectUnderTest, "dirPathField", mockDirPathObject);
nameOfFile = "name.txt";
textToWrite = "some text";
}
#Test
public void shouldCreateAFile(){
//create file and write test
objectUnderTest.createFile(nameOfFile, textToWrite);
/* this method creates the file in mockPathObject and performs
FileWriter.write(text);
FileWriter.close();
*/
File expect = new File(mockPathObject + "\\" + nameOfFile);
assertTrue(expect.exist());
//assert if text is in the file -> it will not be called if first assert fails
}
#After
public void tearDown(){
File destroyFile = new File(mockPathObject + "\\" + nameOfFile);
File destroyDir = new File(mockPathObject);
//here's my problem
destroyFile.delete(); //why is this returning false?
destroyDir.delete(); //will also return false since file was not deleted above
}
I was able to delete the File using deleteOnExit() but I will not be able to delete the directory using delete or deleteOnExit. I will also perform other test for other scenarios in this test script so I don't want to use deleteOnExit.
I don't know why I cannot delete it in JUnit test script while I can delete a file created and modified by FileWriter in runtime when the code is not a JUnit test. I also tried performing an infiniteLoop after the test method and delete the file manually but it tells me that other program is still using the file though I'm able to modify its content.
Hope somebody can suggest a way to delete the files and directories created during the tests. Thanks :D
For more clarity, the method I test looks like this Unit testing method that invokes FileWriter
Edit:Here is the method to test
public void createFile(String fileName, String text){
//SOME_PATH is a static string which is a field of the class
File dir = new File(SOME_PATH); //I modified SOME_PATH using whitebox for testing
if(!dir.exists()){
booelan createDir = dir.mkdirs();
if(!createDir){
sysout("cannot make dir");
return;
}
}
try{
FileWriter fileWrite = new FileWriter(dir.getAbsolutePath() + "/" + fileName, true);
fileWrite.write(text);
fileWrite.close();
}
catch(Exception e){
e.printStackTrace();
}
}
I cannot modify this method as other developers created it. I was just instructed to create unit tests for test automation. Thanks.
Use the #Rule annotation and the TemporaryFolder classfor the folder that you need to delete.
http://kentbeck.github.com/junit/javadoc/4.10/org/junit/Rule.html (404 not found)
Update example of usage by http://junit.org/junit4/javadoc/4.12/org/junit/rules/TemporaryFolder.html:
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");
// ...
}
}
This is how I usually clean up files:
#AfterClass
public static void clean() {
File dir = new File(DIR_PATH);
for (File file:dir.listFiles()) {
file.delete();
}
dir.delete();
}
Your directory must be empty in order to delete it, make sure no other test methods are creating more files there.
Ensure you are closing the FileWriter instance in a finally block.
Ensure that the method objectUnderTest.createFile(nameOfFile, textToWrite) actually closes any opened streams?
I think the best approche is te delete after JVM exit :
Path tmp = Files.createTempDirectory(null);
tmp.toFile().deleteOnExit();