Python:为单元测试创建一个包含文件的模拟或假目录
Python: Creating a mock or fake directory with files for unittesting
我正在尝试为以下功能创建单元测试:
def my_function(path):
#Search files at the given path
for file in os.listdir(path):
if file.endswith(".json"):
#Search for file i'm looking for
if file == "file_im_looking_for.json":
#Open file
os.chdir(path)
json_file=json.load(open(file))
print json_file["name"]
但是,我无法成功创建包含文件的虚假目录,以使该功能正常工作,而不是出现错误。
以下是我到目前为止的内容,但它对我不起作用,我不确定如何将 "file_im_looking_for" 作为文件合并到假目录中。
tmpfilepath = os.path.join(tempfile.gettempdir(), "tmp-testfile")
@mock.patch('my_module.os')
def test_my_function(self):
# make the file 'exist'
mock_path.endswith.return_value = True
file_im_looking_for=[{
"name": "test_json_file",
"type": "General"
}]
my_module.my_function("tmpfilepath")
感谢任何我出错的建议或解决此问题的其他想法!
首先,您忘记将模拟对象传递给测试函数。在你的测试中正确使用 mock 的方法应该是这样的。
@mock.patch('my_module.os')
def test_my_function(self, mock_path):
无论如何,你不应该嘲笑endswith
,而是listdir
。下面的代码片段是一个示例,可能会对您有所帮助。
app.py
def check_files(path):
files = []
for _file in os.listdir(path):
if _file.endswith('.json'):
files.append(_file)
return files
test_app.py
import unittest
import mock
from app import check_files
class TestCheckFile(unittest.TestCase):
@mock.patch('app.os.listdir')
def test_check_file_should_succeed(self, mock_listdir):
mock_listdir.return_value = ['a.json', 'b.json', 'c.json', 'd.txt']
files = check_files('.')
self.assertEqual(3, len(files))
@mock.patch('app.os.listdir')
def test_check_file_should_fail(self, mock_listdir):
mock_listdir.return_value = ['a.json', 'b.json', 'c.json', 'd.txt']
files = check_files('.')
self.assertNotEqual(2, len(files))
if __name__ == '__main__':
unittest.main()
编辑:回答您在评论中的问题,您需要从您的应用中模拟 json.loads
和 open
。
@mock.patch('converter.open')
@mock.patch('converter.json.loads')
@mock.patch('converter.os.listdir')
def test_check_file_load_json_should_succeed(self, mock_listdir, mock_json_loads, mock_open):
mock_listdir.return_value = ['a.json', 'file_im_looking_for.json', 'd.txt']
mock_json_loads.return_value = [{"name": "test_json_file", "type": "General"}]
files = check_files('.')
self.assertEqual(1, len(files))
但是记住!如果您的内容太宽泛或难以维护,也许重构您的 API 应该是个好主意。
我建议使用 Python 的 tempfile library,特别是 TemporaryDirectory
.
您和 Mauro Baraldi 的解决方案的问题是您必须修补多个函数。这是一种非常容易出错的方法,因为使用 mock.patch
你必须确切地知道你在做什么!否则,这可能会导致意外错误并最终导致失败。
就我个人而言,我更喜欢 pytest,因为它具有 IMO 更好的语法和更好的固定装置,但由于创建者使用了 unittest
,我会坚持使用它。
我会像这样重写你的测试代码:
import json
import pathlib
import tempfile
import unittest
wrong_data = {
"name": "wrong_json_file",
"type": "Fake"
}
correct_data = {
"name": "test_json_file",
"type": "General"
}
class TestMyFunction(unittest.TestCase):
def setUp(self):
""" Called before every test. """
self._temp_dir = tempfile.TemporaryDirectory()
temp_path = pathlib.Path(self._temp_dir.name)
self._create_temporary_file_with_json_data(temp_path / 'wrong_json_file.json', wrong_data)
self._create_temporary_file_with_json_data(temp_path / 'file_im_looking_for.json', correct_data)
def tearDown(self):
""" Called after every test. """
self._temp_dir.cleanup()
def _create_temporary_file_with_json_data(self, file_path, json_data):
with open(file_path, 'w') as ifile:
ifile.write(json.dumps(content))
def test_my_function(self):
my_module.my_function(str(self._temp_dir))
您看到您的实际测试被压缩到一行!诚然,没有 assert
,但如果您的函数 return 某些东西,结果将按预期运行。
不要嘲笑,因为一切都存在,之后会被清理干净。最棒的是,您现在可以添加更多测试,进入门槛更低。
我正在尝试为以下功能创建单元测试:
def my_function(path):
#Search files at the given path
for file in os.listdir(path):
if file.endswith(".json"):
#Search for file i'm looking for
if file == "file_im_looking_for.json":
#Open file
os.chdir(path)
json_file=json.load(open(file))
print json_file["name"]
但是,我无法成功创建包含文件的虚假目录,以使该功能正常工作,而不是出现错误。
以下是我到目前为止的内容,但它对我不起作用,我不确定如何将 "file_im_looking_for" 作为文件合并到假目录中。
tmpfilepath = os.path.join(tempfile.gettempdir(), "tmp-testfile")
@mock.patch('my_module.os')
def test_my_function(self):
# make the file 'exist'
mock_path.endswith.return_value = True
file_im_looking_for=[{
"name": "test_json_file",
"type": "General"
}]
my_module.my_function("tmpfilepath")
感谢任何我出错的建议或解决此问题的其他想法!
首先,您忘记将模拟对象传递给测试函数。在你的测试中正确使用 mock 的方法应该是这样的。
@mock.patch('my_module.os')
def test_my_function(self, mock_path):
无论如何,你不应该嘲笑endswith
,而是listdir
。下面的代码片段是一个示例,可能会对您有所帮助。
app.py
def check_files(path):
files = []
for _file in os.listdir(path):
if _file.endswith('.json'):
files.append(_file)
return files
test_app.py
import unittest
import mock
from app import check_files
class TestCheckFile(unittest.TestCase):
@mock.patch('app.os.listdir')
def test_check_file_should_succeed(self, mock_listdir):
mock_listdir.return_value = ['a.json', 'b.json', 'c.json', 'd.txt']
files = check_files('.')
self.assertEqual(3, len(files))
@mock.patch('app.os.listdir')
def test_check_file_should_fail(self, mock_listdir):
mock_listdir.return_value = ['a.json', 'b.json', 'c.json', 'd.txt']
files = check_files('.')
self.assertNotEqual(2, len(files))
if __name__ == '__main__':
unittest.main()
编辑:回答您在评论中的问题,您需要从您的应用中模拟 json.loads
和 open
。
@mock.patch('converter.open')
@mock.patch('converter.json.loads')
@mock.patch('converter.os.listdir')
def test_check_file_load_json_should_succeed(self, mock_listdir, mock_json_loads, mock_open):
mock_listdir.return_value = ['a.json', 'file_im_looking_for.json', 'd.txt']
mock_json_loads.return_value = [{"name": "test_json_file", "type": "General"}]
files = check_files('.')
self.assertEqual(1, len(files))
但是记住!如果您的内容太宽泛或难以维护,也许重构您的 API 应该是个好主意。
我建议使用 Python 的 tempfile library,特别是 TemporaryDirectory
.
您和 Mauro Baraldi 的解决方案的问题是您必须修补多个函数。这是一种非常容易出错的方法,因为使用 mock.patch
你必须确切地知道你在做什么!否则,这可能会导致意外错误并最终导致失败。
就我个人而言,我更喜欢 pytest,因为它具有 IMO 更好的语法和更好的固定装置,但由于创建者使用了 unittest
,我会坚持使用它。
我会像这样重写你的测试代码:
import json
import pathlib
import tempfile
import unittest
wrong_data = {
"name": "wrong_json_file",
"type": "Fake"
}
correct_data = {
"name": "test_json_file",
"type": "General"
}
class TestMyFunction(unittest.TestCase):
def setUp(self):
""" Called before every test. """
self._temp_dir = tempfile.TemporaryDirectory()
temp_path = pathlib.Path(self._temp_dir.name)
self._create_temporary_file_with_json_data(temp_path / 'wrong_json_file.json', wrong_data)
self._create_temporary_file_with_json_data(temp_path / 'file_im_looking_for.json', correct_data)
def tearDown(self):
""" Called after every test. """
self._temp_dir.cleanup()
def _create_temporary_file_with_json_data(self, file_path, json_data):
with open(file_path, 'w') as ifile:
ifile.write(json.dumps(content))
def test_my_function(self):
my_module.my_function(str(self._temp_dir))
您看到您的实际测试被压缩到一行!诚然,没有 assert
,但如果您的函数 return 某些东西,结果将按预期运行。
不要嘲笑,因为一切都存在,之后会被清理干净。最棒的是,您现在可以添加更多测试,进入门槛更低。