Pytest mock pandas read_excel 里面的 Class 构造

Pytest mock pandas read_excel inside the Class Construction

我正在使用 Pytest 进行一些集成测试。我正在读取来自 class 构造函数的 excel sheet 输入。我不知道如何模拟 pandas 的 read_excel 函数。 我的实际代码,

import pandas as pd

class Sample:
    def __init__(self, input_file_path):
        self.df_input = pd.read_excel(
                        input_file_path, sheet_name="Input sheet", header=[0]
                )
        
    def calculation(self):
        pass
    def choose_template(self):
        pass
    

sample_obj = Sample('./test.xlsx')
sample_obj.choose_template()

test_one.py,

@mock.patch.dict(os.environ, {"DB_CONNECTION_STRING": "Mock_Connection_String", 
                              "DB_DATABASE": "Mock_Db", 
                              "AZURE_STORAGE_CONNECTION_STRING": "Mock_Azure_connection_String", 
                              },clear=True)



def test_choose_template_valid():
    expected = "fizz"
    sample_obj = Sample('./test.xlsx')

    sample_obj.choose_template()
    actual = "fizz"
    assert actual == expected

我收到以下错误。我明白为什么会这样。但是我需要一个关于如何模拟 pd.read_excel 调用的解决方案。

=============================================================== short test summary info ================================================================
FAILED test_report.py::test_choose_template_valid - FileNotFoundError: [Errno 2] No such file or directory: './test.xlsx'
================================================================== 1 failed in 0.96s ===================================================================

您的主要问题是您的代码在导入模块时已经执行。在这种情况下,您不能在调用它之前模拟它。

在通常的 if __name__ == "__main__" 检查后调用该代码,或者更好的是,移入一个函数,并在检查后调用该函数:

class Sample:
  ...

def choose_sample_template(file_name):
    sample_obj = Sample(file_name)
    return sample_obj.choose_template()

if __name__ == "__main__":
    choose_sample_template('./test.xlsx')

现在您可以在测试该功能时以通常的方式模拟 pd.read_excel

@mock.patch.dict(...)
@mock.patch("your_module.pd.read_excel")
def test_choose_template_valid(mocked_read):
    mocked_read.return_value = ... # some df
    expected = "fizz"   
    actual = choose_sample_template('./test.xlsx')
    assert actual == expected

其中 your_module 是定义 Sample 的模块。

如果您在测试的调用中多次调用 pd.read_excel,如评论中所述,您可以使用 side_effect 而不是 return_value:

@mock.patch.dict(...)
@mock.patch("your_module.pd.read_excel")
def test_choose_template_valid(mocked_read):
    mocked_read.side_effect = [
       some_df,
       second_df,
       third_df
    ]
    ...