在Python单元测试和主代码中访问资源文件

14 投票
5 回答
15456 浏览
提问于 2025-04-16 21:07

我有一个Python项目,目录结构如下:

project/
project/src/
project/src/somecode.py
project/src/mypackage/mymodule.py
project/src/resources/
project/src/resources/datafile1.txt

在mymodule.py文件里,我有一个类(我们叫它"MyClass"),这个类需要加载datafile1.txt文件。当我这样写的时候:

open ("../resources/datafile1.txt")

这个方法在从somecode.py运行代码时还算有效。

但是问题来了,我在mymodule.py里有单元测试,这些测试也是在这个文件里定义的。如果我继续使用上面那种相对路径,单元测试就会出问题,因为此时代码是从project/src/mypackage运行的,而不是从project/src运行的,这样相对路径就不对了。

有没有什么好的建议来解决这个问题?如果我把测试用例移动到project/src文件夹里,又会让主源文件夹变得杂乱,因为里面会有测试用例。

5 个回答

7

在上面的回答基础上,我想再补充一些Python 3的小技巧,让你的测试代码更简洁。

借助于pathlib这个库,你可以在测试中更清晰地导入资源。它还能处理Unix系统(使用/)和Windows系统(使用\)之间的路径分隔符差异。

假设我们有这样的文件夹结构:

`-- tests
    |-- test_1.py <-- You are here !
    |-- test_2.py
    `-- images
        |-- fernando1.jpg <-- You want to import this image !
        `-- fernando2.jpg

你现在在test_1.py这个文件里,想要导入fernando1.jpg。使用pathlib库,你可以用面向对象的方式来读取你的测试资源,像这样:

from pathlib import Path

current_path = Path(os.path.dirname(os.path.realpath(__file__)))
image_path = current_path / "images" / "fernando1.jpg"

with image_path.open(mode='rb') as image :
    # do what you want with your image object

不过其实还有一些方便的方法,可以让你的代码比mode='rb'更清晰,比如:

image_path.read_bytes() # Which reads bytes of an object

text_file_path.read_text() # Which returns you text file content as a string

就这样!

12

我通常用这个方法来获取我模块的相对路径。不过我从来没有在单元测试中试过。

import os

print(os.path.join(os.path.dirname(__file__), 
                   '..',
                   'resources' 
                   'datafile1.txt'))

注意:使用 .. 这种方式效果很好,但如果你改变了目录结构,就需要更新这一部分了。

5

在每个包含Python脚本的文件夹里,放一个Python模块,这个模块知道整个文件结构的根路径。你可以在这个模块里定义一个全局变量,存储相对路径。然后在每个脚本中导入这个模块。Python会先在当前目录中查找,所以它总是会使用当前目录下的模块版本,这个版本会有指向当前目录根路径的相对路径。接着,你就可以用这个路径去找到其他文件。例如:

# rootpath.py
rootpath = "../../../"

# in your scripts
from rootpath import rootpath
datapath = os.path.join(rootpath, "src/resources/datafile1.txt")

如果你不想在每个目录里放额外的模块,你可以试试这种方法:

在目录结构的顶层放一个标记文件,比如 thisisthetop.txt。让你的Python脚本向上移动目录,直到找到这个文件。然后所有的路径都相对于这个目录来写。

你可能已经在项目目录里有某个文件可以用来做这个(比如一直向上移动,直到找到一个 src 目录),或者你可以给项目目录起个名字,让它更明显。

撰写回答