【问题标题】:Python: Creating a mock or fake directory with files for unittestingPython:使用文件创建模拟或假目录以进行单元测试
【发布时间】:2016-05-11 10:20:15
【问题描述】:

我正在尝试为以下函数创建单元测试:

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”作为文件合并到 fake 目录中。

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")

感谢任何我出错的建议或解决此问题的其他想法!

【问题讨论】:

    标签: python unit-testing mocking


    【解决方案1】:

    首先,您忘记将模拟对象传递给测试函数。在你的测试中使用 mock 的正确方法应该是这样的。

    @mock.patch('my_module.os')
    def test_my_function(self, mock_path):
    

    无论如何,你不应该嘲笑endswith,而应该嘲笑listdir。下面的 sn-p 是一个示例,可能会对您有所帮助。

    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.loadsopen

    @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 过于宽泛或难以维护,也许重构您的 API 应该是一个好主意。

    【讨论】:

    • 非常感谢您的解释,但是我如何使模拟目录中的例如'b.json'等于“file_im_looking_for”的结构(在我的代码中显示)
    • 最后一个问题,对于 mock_json_loads 和 mock_listdir 的返回值似乎混淆了?使用打印语句,似乎“for _file in os.listdir(path):”行返回 [{"name": "test_json_file", "type": "General"}] 当它应该返回 ['a.json' , 'file_im_looking_for.json', 'd.txt']。你知道为什么会这样吗?
    • 没关系我的@mock.patch 顺序错误!
    • 这是mock中的一个重要行为,它的声明和打补丁的顺序。
    • 是否可以模拟创建的假文件的内容?
    【解决方案2】:

    我建议使用 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,但如果你的函数会返回一些东西,结果就会像预期的那样运行。

    没有嘲笑,因为一切都存在并且之后会被清理。最好的一点是,您现在可以添加更多测试,进入门槛更低。

    【讨论】:

      猜你喜欢
      • 2020-03-28
      • 2016-12-31
      • 2023-01-18
      • 1970-01-01
      • 2011-11-26
      • 2015-07-01
      • 1970-01-01
      • 1970-01-01
      • 2012-02-16
      相关资源
      最近更新 更多