在Python单元测试和主代码中访问资源文件
我有一个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 个回答
在上面的回答基础上,我想再补充一些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
就这样!
我通常用这个方法来获取我模块的相对路径。不过我从来没有在单元测试中试过。
import os
print(os.path.join(os.path.dirname(__file__),
'..',
'resources'
'datafile1.txt'))
注意:使用 .. 这种方式效果很好,但如果你改变了目录结构,就需要更新这一部分了。
在每个包含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
目录),或者你可以给项目目录起个名字,让它更明显。