What is the correct way to write to temp file during unit tests with Maven?
Asked Answered
P

8

84

I have written a unit test that writes a file to the file-system, given no path it writes to the working directory; so if executed from the project directory it writes in the project root, if in the projects parent directory it writes to the parents root directory.

So what is the correct way to write to the target directory? Quite possibly a directory inside the target directory?

If I quite simply specify target/ with the file it will write to the parent projects target instead of the projects target.

UPDATE: I actually want the file after the test finishes. The file is for an extraction format for third-parties that needs to be sent to the third parties. The test can be switched on/off to allow me to only run if the format of the file changes for re-approval. It's not a huge problem where the file goes, but I would like something that's easy to find.

Personal answered 13/9, 2012 at 5:59 Comment(0)
U
84

You could try to use TemporaryFolder JUnit @Rule as described here

The TemporaryFolder creates a folder in the default temporary file directory specified by the system property java.io.tmpdir. The method newFile creates a new file in the temporary directory and newFolder creates a new folder.

When the test method finishes, JUnit automatically deletes all files and directories in and including the TemporaryFolder. JUnit guarantees to delete the resources, whether the test passes or fails.


After Question Updated

You can change the working directory used by maven-surefire-plugin.

<plugins>
    [...]
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-surefire-plugin</artifactId>
        <version>2.12.3</version>
        <configuration>
          <workingDirectory>${project.build.directory}</workingDirectory>
        </configuration>
      </plugin>
    [...]
</plugins>

You can change that working directory to anything you need for your tests like ${project.build.directory}/my_special_dir/.

The working directory in surefire plugin only affects tests being run and ONLY for tests being conducted by maven. If you run your tests from within an IDE the working directory will be something else.

Uraemia answered 13/9, 2012 at 6:28 Comment(7)
Thanks maba, this is a similar problem to Bohemian, the file ends up in an obscure location. I actually want the file after the test finishes.Personal
@BrettRyan OK, updated with how to change working directory for the surefire plugin. That should do the trick.Uraemia
Fantastic, thanks maba, that's perfect and will always be consistent on all machines that the test is run on.Personal
Thanks for leaving the original answer. Thats what I needed.Weakwilled
JUNIT does not guarantee automatic deletion you have to do it @Rule public TemporaryFolder folder = TemporaryFolder.builder().assureDeletion().build();Roxanneroxburgh
@Uraemia JUnit TemporaryFolder. Please clarify the above comment. It does seem not to guarantee deletion.Hemiplegia
For JUnit5, see this articleCircumlunar
F
66

No need to reinvent the wheel...

The JDK provides a way to a create temporary file, and a way to automatically delete it on exit:

File file = File.createTempFile( "some-prefix", "some-ext");
file.deleteOnExit();

Use the file and it will be deleted automatically when your test finishes. That's all there is to it.

To specify the directory to use for temporary files, use the overloaded method:

File file = File.createTempFile( "prefix", "ext", new File("/some/dir/path"));
Fahrenheit answered 13/9, 2012 at 6:28 Comment(4)
Thanks Bohemian, I actually want the file to remain around after exit, so I can send it on to the vendor. I suppose the system temp directory is fine though I did hope to have it in the target directory.Personal
I've just attempted this and it turns out the file is in such an obscure place it wouldn't be wise to use. Ended up in /var/folders/xf/scvh7yr11m1glr4b663n1hcsnv5lf0/T/test2614442099335581603.xmlPersonal
@BrettRyan No problem - you can specify the directory that is used for temp files - see edited answerFahrenheit
Thanks Bohemian, I do see merit in being able to set the temp directory, though I do like the ability of having this in a reliable location under the target directory as opposed to requiring a specific location existing on the machine.Personal
W
6

Since JUnit 5 you can use @TempDir. It will be deleted (including its content) after the test(s).

either one temporary directory for every test:

@Test
void testWithTempFiles(@TempDir Path tempDir) 
    Path file = tempDir.resolve("temp_file.txt");
    ...
}

or a single one for all tests in the class:

@TempDir
static Path tempDir

@Test
void testWithTempFiles() 
    Path file = tempDir.resolve("temp_file.txt");
    ...
}
Webbed answered 20/5, 2022 at 11:26 Comment(1)
This is the most elegant and modern solution currently.Condensable
B
2

I would write a routine which determines where the file should be written to, which i unitest afterwards, in general i try to avoid (as possible) accessing persistent data in unittests, like file IO or database access, this has performance reasons and others too.

Take a look at this asnwer: https://mcmap.net/q/246746/-unit-testing-file-i-o

Belostok answered 13/9, 2012 at 6:6 Comment(2)
Thanks O.D, I've never had to do such a thing until now. I wrote the test to write a file that will be sent to other vendors in production code. My test writes the file to disk for me to send to vendors to get approval, once approved the test won't be needed but could be useful later when something changes and need to "re-approve" the output.Personal
but why are you using a "unit test" for such purpose? I think even have a separate class with main() method to serve for such purpose is even more appropriate than abusing unit testTube
D
2

You will want to be able to run your tests both from an IDE as well as from Maven, so best practice is to write your tests so that they do not assume they are being run within Maven.

One of the best ways to deal with temporary files is using junit rules. This allows you to rely on the rule to clean up for you.

Disdain answered 13/9, 2012 at 6:28 Comment(0)
T
2

To state it beforehand, I am strongly against doing such things in unit test. I haven't really tried this but it should work:

Assume you are using surefire plugin, from the document it quoted that you can access the base directory of project under test by System.getProperty("basedir"). Get it and create files under basedir/target.

A more "appropriate" way (as we may have chance that we configured output directory to something else, you can change the surefire plugin config to something like this:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-surefire-plugin</artifactId>
    <version>2.6</version>
    <configuration>
        <systemPropertyVariables>
            <myOutDir>${project.build.outputDirectory}</myOutDir>
        </systemPropertyVariables>
    </configuration>
</plugin>

Then you can get the actual output directory by System.getProperty("myOutDir") in your test.

Tube answered 13/9, 2012 at 9:39 Comment(0)
A
1

Temporary files should be created into temporary directory. Retrieve it using call System.getProperty("java.io.tmpdir")

Temporary file should be temporary, i.e. should be removed when it is not needed. To acheive this do not forget to delete it in finally block or in code that runs after the test, i.e.:

File tmpFile = ...
try {
   // deal with tempFile
} finally {
    tempFile.delete();
}

and/or

public class MyTestCase {
    private File tmpFile = null;

    @Before
    public void setUp() {
        tmpFile = ...;
    }

    @Test
    public void setUp() {
        // deal with tmpFile
    }

    @After
    public void setUp() {
        tmpFile.delete();
    }
}

Use File.createTempFile(String prefix, String suffix) if it is possible, i.e. you can then use file with special name generated by createTempFile().

Use file.deleteOnExit(). This creates hook that removes file automatically when JVM is terminated. The file will remain only if JVM was killed with kill -9, so it did not have a chance to run shutdown code.

Abstergent answered 13/9, 2012 at 6:20 Comment(0)
A
0

Here are several ways using JUnit 5 and Kotlin language.

  • Using JUnit 5 @TempDir on a field:

    @TempDir(cleanup = ON_SUCCESS)
    lateinit var tempDirectory: Path
    
    @Test fun example() {
        // use the tempDirectory
    }
    
  • Using JUnit 5 @TempDir on a test function parameter:

    @Test fun example(
        @TempDir(cleanup = CleanupMode.ON_SUCCESS) tempDirectory: Path
    ) {
        // use the tempDirectory
    }
    
  • Manually creating the file and calling deleteOnExit():

    @Test fun example() {
        val file = File("file.txt").apply { deleteOnExit() }
    }
    
  • Using Kotlin createTempDirectory and createTempFile functions in test class or test function:

    val file = createTempFile("file.txt")
    
Agustinaah answered 14/9, 2023 at 8:19 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.