Python: create temporary files and directories in unittest - Adam Johnson (original) (raw)

2024-12-30A rather temporary bird.

Sometimes, tests need temporary files or directories. You can do this in Python’s unittest with the standard library tempfile module. Let’s look at some recipes to do so within individual tests and setUp().

Creating a temporary file

To create a temporary file in a single test:

from tempfile import NamedTemporaryFile from unittest import TestCase

class ExampleTests(TestCase): def test_example(self): temp_file = self.enterContext( NamedTemporaryFile(mode="w+", suffix=".html"), )

    # Write to the file
    temp_file.write("<h1>Cosmic Crisp</h1>")

    # Read from the file
    temp_file.seek(0)
    result = temp_file.read()

    self.assertEqual(result, "<h1>Cosmic Crisp</h1>")

Note:

Creating a temporary directory

This works similarly:

from tempfile import TemporaryDirectory from unittest import TestCase

class ExampleTests(TestCase): def test_example(self): temp_dir = self.enterContext(TemporaryDirectory())

    # Write to a file
    with open(f"{temp_dir}/apple.html", "w") as temp_file:
        temp_file.write("<h1>Cosmic Crisp</h1>")

    # Read from the file
    with open(f"{temp_dir}/apple.html", "r") as temp_file:
        result = temp_file.read()

    self.assertEqual(result, "<h1>Cosmic Crisp</h1>")

Note:

Per-test usage in setUp()

To have a temporary file available for each test, hoist the self.enterContext() to setUp():

from tempfile import NamedTemporaryFile from unittest import TestCase

class ExampleTests(TestCase): def setUp(self): super().setUp() self.temp_file = self.enterContext( NamedTemporaryFile(mode="w+", suffix=".html") )

def test_example(self):
    # Write to the file
    self.temp_file.write("<h1>Cosmic Crisp</h1>")
    self.temp_file.seek(0)

    # Read from the file
    result = self.temp_file.read()

    self.assertEqual(result, "<h1>Cosmic Crisp</h1>")

Or, for a temporary directory:

from tempfile import TemporaryDirectory from unittest import TestCase

class ExampleTests(TestCase): def setUp(self): super().setUp() self.temp_dir = self.enterContext(TemporaryDirectory())

def test_example(self):
    # Write to a file
    with open(f"{self.temp_dir}/apple.html", "w") as temp_file:
        temp_file.write("<h1>Cosmic Crisp</h1>")

    # Read from the file
    with open(f"{self.temp_dir}/apple.html", "r") as temp_file:
        result = temp_file.read()

    self.assertEqual(result, "<h1>Cosmic Crisp</h1>")

Sprinkle in some pathlib

Using pathlib.Path simplifies the creation of test files within a temporary directory:

from pathlib import Path from tempfile import TemporaryDirectory from unittest import TestCase

class ExampleTests(TestCase): def setUp(self): super().setUp()

    self.temp_path = Path(self.enterContext(TemporaryDirectory()))

def test_example(self):
    # Write to a file
    (self.temp_path / "apple.html").write_text("<h1>Cosmic Crisp</h1>")

    # Read from the file
    result = (self.temp_path / "apple.html").read_text()

    self.assertEqual(result, "<h1>Cosmic Crisp</h1>")

A bonus tip: pair with textwrap.dedent() when creating multi-line files. This can keep the text neatly indented within your test method but dedented in the temporary file:

from pathlib import Path from tempfile import TemporaryDirectory from textwrap import dedent from unittest import TestCase

class ExampleTests(TestCase): def setUp(self): super().setUp()

    self.temp_path = Path(self.enterContext(TemporaryDirectory()))

def test_example(self):
    # Write to a file
    (self.temp_path / "apple.html").write_text(
        dedent(
            """
            <html>
              <body>
                <h1>Cosmic Crisp</h1>
              </body>
            </html>
            """
        )
    )

    # Read from the file
    result = (self.temp_path / "apple.html").read_text()

    self.assertIn("<h1>Cosmic Crisp</h1>", result)

Debug with delete=False

If tests are failing, you may wish to inspect the contents of a temporary file or directory. To disable the cleanup, add delete=False to the context manager, plus a print() to display the path. For example, for a file:

def setUp(self):
    super().setUp()
    self.temp_file = self.enterContext(

Then when tests fail, the emoji will highlight the path (one of my print debugging tips) and you can inspect the file:

$ python -m unittest example 😅 /var/folders/20/lzgtdyzs7wj5fc_90w1h3/T/tmpgpynu7f_.html F

FAIL: test_example (example.ExampleTests.test_example)

... FAILED (failures=1)

$ cat /var/folders/20/lzgtdyzs7wj5fc_90w1h3/T/tmpgpynu7f_.html

Cosmik Crisp

delete=False works similarly for TemporaryDirectory.

Fin

May your data be temporary and your tests pass forever,

—Adam


Read my book Boost Your Git DX to Git better.


One summary email a week, no spam, I pinky promise.

Related posts:

Tags: python, unittest