我有一个我在Eclipse中工作的Python项目,我有以下文件结构:
/Project
/projectname
module1.py
module2.py
# etc.
/test
testModule1.py
# etc.
testdata.csv
在我的一个测试中,我创建了一个给出'testdata.csv'
作为参数的类的实例。该对象执行open('testdata.csv')
并读取内容。
如果我只用unittest
运行这个单个测试文件,一切正常,文件就可以找到并正确读取。但是,如果我尝试运行所有单元测试(即通过右键单击test
目录而不是单个测试文件运行),则会收到无法找到文件的错误。
有没有办法绕过这个(除了提供绝对路径,我不想这样做)?
答案 0 :(得分:22)
通常我所做的是定义
THIS_DIR = os.path.dirname(os.path.abspath(__file__))
位于每个测试模块的顶部。然后,您所处的工作目录并不重要 - 文件路径始终与测试模块所在的位置相同。
然后我在我的测试(或测试设置)中使用这样的东西:
my_data_path = os.path.join(THIS_DIR, os.pardir, 'data_folder/data.csv')
或者在您的情况下,因为数据源位于测试目录中:
my_data_path = os.path.join(THIS_DIR, 'testdata.csv')
答案 1 :(得分:12)
访问文件系统的单元测试通常不是一个好主意。这是因为测试应该是自包含的,通过将测试数据放在测试之外,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)
答案 2 :(得分:0)
我认为处理这些情况的最好方法是通过控制反转进行编程。
在下面的两节中,我主要显示无反转控制解决方案的外观。第二部分显示了控制反转的解决方案,以及如何在没有模拟框架的情况下测试此代码。
最后,我指出一些个人利与弊,一点都不是正确无误的。随意评论以进行扩充和更正。
您有一个使用python中的std open
方法的类。
class UsesOpen(object):
def some_method(self, path):
with open(path) as f:
process(f)
# how the class is being used in the open
def main():
uses_open = UsesOpen()
uses_open.some_method('/my/path')
这里我在代码中显式使用了open
,因此为其编写测试的唯一方法是使用显式测试数据(文件)或使用沙丘建议的模拟框架。
但是还有另一种方法:
现在,我以不同的方式重写了课堂:
class UsesOpen(object):
def __init__(self, myopen):
self.__open = myopen
def some_method(self, path):
with self.__open(path) as f:
process(f)
# how the class is being used in the open
def main():
uses_open = UsesOpen(open)
uses_open.some_method('/my/path')
在第二个示例中,我将open
的依赖项注入到构造函数中(构造函数依赖项注入)。
现在,我可以轻松编写测试并在需要时使用open
的测试版本:
EXAMPLE_CONTENT = """my file content
as an example
this can be anything"""
TEST_FILES = {
'/my/long/fake/path/to/a/file.conf': EXAMPLE_CONTENT
}
class MockFile(object):
def __init__(self, content):
self.__content = content
def read(self):
return self.__content
def __enter__(self):
return self
def __exit__(self, type, value, tb):
pass
class MockFileOpener(object):
def __init__(self, test_files):
self.__test_files = test_files
def open(self, path, *args, **kwargs):
return MockFile(self.__test_files[path])
class TestUsesOpen(object):
def test_some_method(self):
test_opener = MockFileOpener(TEST_FILES)
uses_open = UsesOpen(test_opener.open)
# assert that uses_open.some_method('/my/long/fake/path/to/a/file.conf')
# does the right thing
专业依赖注入
Con依赖注入
答案 3 :(得分:-3)
您的测试不应直接打开文件,每个测试都应复制文件并使用其副本。