单元测试模拟 pandas to_csv

Unittest mock pandas to_csv

mymodule.py

def write_df_to_csv(self, df, modified_fn):
    new_csv = self.path + "/" + modified_fn
    df.to_csv(new_csv, sep=";", encoding='utf-8', index=False)

test_mymodule.py

class TestMyModule(unittest.TestCase):
    def setUp(self):
        args = parse_args(["-f", "test1"])
        self.mm = MyModule(args)
        self.mm.path = "Random/path"

    self.test_df = pd.DataFrame(
                [
                    ["bob", "a"],
                    ["sue", "b"],
                    ["sue", "c"],
                    ["joe", "c"],
                    ["bill", "d"],
                    ["max", "b"],
                ],
                columns=["A", "B"],
            )

def test_write_df_to_csv(self):
    to_csv_mock = mock.MagicMock()
    with mock.patch("project.mymodule.to_csv", to_csv_mock, create=True):
        self.mm.write_df_to_csv(self.test_df, "Stuff.csv")
    to_csv_mock.assert_called_with(self.mm.path + "/" + "Stuff.csv")

当我 运行 这个测试时,我得到:

FileNotFoundError: [Errno 2] No such file or directory: 'Random/path/Stuff.csv'

我试图在我的方法中模拟 to_csv。我的其他测试 运行 符合预期,但是我不确定此测试哪里出错了。我对 MagicMock 的使用是否正确,还是我忽略了其他东西?

您必须以带有正斜杠的 a 开始路径。这是更正后的代码:

self.mm.path = "/Random/path/"

同时更改:

to_csv_mock.assert_called_with(self.mm.path + "Stuff.csv")

编辑:正如@Thomas所说,我不确定project.mymodule.to_csv是否正确!

您没有提供最小的、可重现的示例,所以我不得不去掉一些东西才能使它起作用。我想你可以自己填写缺失的部分。

一个问题是 mock.patch("project.mymodule.to_csv", ...),它试图在 模块 [=] 中模拟一个名为 to_csvclass 55=] 在导入路径 project.mymodule。这只是“有效”,因为你通过了 create=True,但当然模拟以前不存在的东西没有效果,因为没有人会调用它。

可以 使用 mock.patch("pandas.DataFrame", ...) 模拟整个 DataFrame class。注意:无论您如何(甚至是否)在当前模块中导入 pandas,它都不是 pd

但是你的单元测试将断言 to_csv 是在 any DataFrame 上调用的,不一定是你传入的那个。通过模拟 只是我们传递给write_df_to_csvDataFrame对象上的to_csv方法,测试变得更全面,也更容易理解。我们可以使用 mock.patch.object.

来做到这一点

mock.patch.object returns 模拟函数,我们随后可以在其上调用断言。因为它是一个 mock 方法,而不是一个自由函数,所以我们不需要在断言中传递 self 参数。

project/mymodule.py

def write_df_to_csv(df, file_name):
    df.to_csv(file_name, sep=";", encoding='utf-8', index=False)

project/test_mymodule.py

import unittest.mock as mock
import unittest

import pandas as pd

import project.mymodule as mm

class TestMyModule(unittest.TestCase):

    def test_write_df_to_csv(self):
        test_df = pd.DataFrame(...)
        with mock.patch.object(test_df, "to_csv") as to_csv_mock:
            mm.write_df_to_csv(test_df, "Stuff.csv")
            to_csv_mock.assert_called_with("Stuff.csv")

if __name__ == '__main__':
    unittest.main()

输出

测试现在以正确的方式失败了,因为参数实际上不匹配!

$ python -m project.test_mymodule
F
======================================================================
FAIL: test_write_df_to_csv (__main__.TestMyModule)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/tmp/project/test_mymodule.py", line 25, in test_write_df_to_csv
    to_csv_mock.assert_called_with("Stuff.csv")
  File "/usr/lib/python3.8/unittest/mock.py", line 913, in assert_called_with
    raise AssertionError(_error_message()) from cause
AssertionError: expected call not found.
Expected: to_csv('Stuff.csv')
Actual: to_csv('Stuff.csv', sep=';', encoding='utf-8', index=False)

----------------------------------------------------------------------
Ran 1 test in 0.003s

FAILED (failures=1)

另一种方法是

import unittest.mock as mock
import unittest
import pandas as pd

import project.mymodule as mm

class TestMyModule(unittest.TestCase):

    def test_write_df_to_csv(self):
        test_df = pd.DataFrame(...)
        with mock.patch("pandas.DataFrame.to_csv") as to_csv_mock:
            mm.write_df_to_csv(test_df, "Stuff.csv")
            to_csv_mock.assert_called_with("Stuff.csv")

if __name__ == '__main__':
    unittest.main()'''