使用外部数据文件的Python单元测试

use*_*959 21 python unit-testing

我有一个我正在Eclipse中工作的Python项目,我有以下文件结构:

/Project
    /projectname
        module1.py
        module2.py 
        # etc.
    /test
        testModule1.py
        # etc.
        testdata.csv
Run Code Online (Sandbox Code Playgroud)

在我的一个测试中,我创建了一个我的类的实例'testdata.csv'作为参数.该对象执行open('testdata.csv')和读取内容.

如果我运行这个单个测试文件,unittest一切正常,找到并正确读取文件.但是,如果我尝试运行所有单元测试(即通过右键单击test目录而不是单个测试文件来运行),则会收到无法找到文件的错误.

有没有办法绕过这个(除了提供绝对路径,我不想这样做)?

Jam*_*ull 31

通常我所做的就是定义

THIS_DIR = os.path.dirname(os.path.abspath(__file__))

在每个测试模块的顶部.然后,您所处的工作目录无关紧要 - 文件路径始终与测试模块所在的位置相同.

然后我在我的测试(或测试设置)中使用这样的东西:

my_data_path = os.path.join(THIS_DIR, os.pardir, 'data_folder/data.csv')
Run Code Online (Sandbox Code Playgroud)

或者在您的情况下,因为数据源位于测试目录中:

my_data_path = os.path.join(THIS_DIR, 'testdata.csv')
Run Code Online (Sandbox Code Playgroud)

  • 在这里也添加此内容,如果您想返回目录,请使用 /../ 示例: template_path = f"{THIS_DIR}/../../my-template.yaml" (2认同)

Dun*_*nes 13

访问文件系统的单元测试通常不是一个好主意.这是因为测试应该是自包含的,通过使测试数据在测试外部,csv文件所属的测试不再是明显的,或者即使它仍然在使用中.

一个更好的解决方案是修补open并使其返回类似文件的对象.

from unittest import TestCase
from unittest.mock import patch, mock_open

from textwrap import dedent

class OpenTest(TestCase):
    DATA = dedent("""
        a,b,c
        x,y,z
        """).strip()

    @patch("builtins.open", mock_open(read_data=DATA))
    def test_open(self):

        # Due to how the patching is done, any module accessing `open' for the 
        # duration of this test get access to a mock instead (not just the test 
        # module).
        with open("filename", "r") as f:
            result = f.read()

        open.assert_called_once_with("filename", "r")
        self.assertEqual(self.DATA, result)
        self.assertEqual("a,b,c\nx,y,z", result)
Run Code Online (Sandbox Code Playgroud)

  • 该库的目的是加载大小恰好为 1.5MB 的二进制数据文件。它是一个文件格式解析器。不过,感谢您的回答:我会尝试将测试分开,但这并不能解决很多问题。我有一些不访问文件系统的单元测试和一些访问文件系统的集成测试。对于后者,我使用了 Jamie Bull 的解决方案。 (5认同)
  • 较大的二进制文件(在我的示例中为1.5 MB)怎么样?我需要测试一个分析二进制数据文件的函数。 (2认同)

use*_*520 6

对于测试发现,建议测试文件夹作为一个包。在这种情况下,您可以使用importlib.resources访问测试文件夹中的资源(注意各个函数的 Python 版本兼容性,有可用的向后移植importlib_resources),如此处所述例如:

import importlib.resources

test_file_path_str = str(importlib.resources.files('tests').joinpath('testdata.csv'))
test_function_expecting_filename(test_file_path_str)
Run Code Online (Sandbox Code Playgroud)

像这样,您不需要依赖于推断代码的文件位置。