[Python] Manage Temporal Directory in unit test

By | 22 7 月, 2023

写单元测试时,经常需要写些文件,进行对比;或者提前生成文件,用于读入测试。要求是:单元测试执行完后,自动清理这些临时目录。

unittest

unittest是Python自带的单元测试框架,需要使用tempfile模块来生成临时文件。

[high-level] setUp创建 – tearDown删除

类似Java的unittest,setUp()方法会在每个test case执行前执行;tearDown()方法会在每个test case执行后执行。

import unittest
import tempfile
class TestExample(unittest.TestCase):
    def setUp(self):
        # Create a temporary directory
        self.test_dir = tempfile.TemporaryFile()
    def tearDown(self):
        # Close the file, the directory will be removed after the test
        self.test_dir.close()
if __name__ == '__main__':
    unittest.main()

[单个test case] with创建

with创建临时文件,离开with代码块后,临时文件会自动被删除。

import unittest
import tempfile
class TestExample(unittest.TestCase):
	def test_example(self):
		with tempfile.TemporaryDirectory() as tmpdirname:
			print('created temporary directory', tmpdirname)
if __name__ == '__main__':
    unittest.main()

pytest

pytest是第三方的测试框架,语法更简洁,不用继承类。

pytest生成的临时路径,不会立马删除,会保留最近的3个临时路径。利用这点,可以手动访问路径,查看里面的内容。

tmp_path fixture – 每个case不同

tmp_path是pathlib.Path对象,每个test case的tmp_path都不同,各不干扰。

def test_method1(tmp_path):
    print(tmp_path / "sub")
    # .../Temp/pytest-of-Administrator/pytest-0/test_method10/sub
def test_method2(tmp_path):
    print(tmp_path / "sub")
    # .../Temp/pytest-of-Administrator/pytest-0/test_method20/sub

tmp_path_factory fixture – 所有test file共享

tmp_path_factory(session-scoped fixture)用于创建临时目录,在整个pytest执行过程中,它是唯一的。下面两个测试文件,它们的单元测试用tmp_path_factory生成一个临时目录,都在同一个目录里。

# hello1_test.py
def test_hello1(tmp_path_factory):
    print(tmp_path_factory.mktemp('sub'))
    # .../Temp/pytest-of-Administrator/pytest-11/sub0
# hello2_test.py
def test_hello2(tmp_path_factory):
    print(tmp_path_factory.mktemp('sub'))
    # .../Temp/pytest-of-Administrator/pytest-11/sub1

利用这个特性,可以在全局初始化一些文件(session-scoped),方便在各个test case里共享使用。下面的例子,image_file()是个自定义fixture,它的返回值会传入给test case里的参数image_file。

import pytest
@pytest.fixture(scope="session")
def image_file(tmp_path_factory):
    img = compute_expensive_image()
    fn = tmp_path_factory.mktemp("data") / "img.png"
    img.save(fn)
    return fn
# contents of test_image.py
def test_histogram(image_file):
    img = load_image(image_file)
    # compute and test histogram