模拟对外部模块的调用函数

Mock a call function to an external module

我有一个 class 对我读取的 pandas 数据帧进行一些验证。 class 看起来像这样(简化了一些东西可能没有意义)

import pandas as pd

class PandasValidator:
    read_kwargs = {'sep'='\t',header=None}
    def __init__(self,path_to_data:str,max_rows:int) -> None:
        self.path = path

def validate_num_rows(self,threshold: float = 0.1) -> bool:
    df_shape = pd.read_csv(self.path,*self.read_kwargs).shape
    return df_shape[0]*threshold <= self.max_rows
    

我想测试方法 validate_num_rows,所以我想修补函数的第一行,我在测试时不读取实际的 df,我的测试看起来像这样(这不是工作代码,我最好的尝试)。

@patch('df.read_csv') #not sure what goes in here
def test_validate_num_rows(mock) -> None:
    mock.shape=(30,30)
    result = PandasValidator('dummy-path',30).validate_num_rows(0.1)
    assert result == True

老实说,我不知道要修补和模拟什么或如何做。我想模拟 validate_num_rows 方法的第一行。我知道重构代码会使测试更容易,但这不是我的选择

你的 class 如果它接受一个数据帧而不是自己读取它会更容易测试。

import pandas as pd

class PandasValidator:
    def __init__(self, df: pd.DataFrame, max_rows: int) -> None:
        self._df = df
        self._max_rows = max_rows

    def validate_num_rows(self, threshold: float = 0.1) -> bool:
        return self._df.shape * threshold <= self._max_rows

现在在你的测试中,你只需要在内存中构造一个数据帧并将其传递给PandasValidator

然后你可以创建另一个从文件中读取数据帧的函数:

import pandas as pd
from pathlib import Path

def read_csv(path: Path) -> pd.DataFrame:
    return pd.read_csv(path, sep='\t', header=None)

如果你想测试这个功能,你可以使用monkeypatch fixture from pytest:

import pandas
import your_module


def test_read_csv(monkeypatch):
    expected_dataframe = # create a dataframe somehow

    def fake_read_csv(path, **kwargs):
        assert path == Path('/foo/bar')
        assert kwargs == {'sep': '\t', 'header': None}
        return expected_dataframe

    monkeypatch.setattr(pandas, "read_csv", fake_read_csv)

    actual_dataframe = your_module.read_csv(Path('/foo/bar'))
    assert actual_dataframe == expected_dataframe

另一种方法是根本不使用 monkeypatching,而是使用 temporary files:

针对真实文件系统测试功能
from pathlib import Path
import your_module

def test_read_csv(tmp_dir: Path):
    csv_path = tmp_dir / "my_fake_csv.csv"
    csv_path.write_text(some_predefined_csv)
    dataframe = your_module.read_csv(csv_path)
    # TODO: assert some properties about your dataframe

有些人会将其描述为集成测试,而不是单元测试,因为它与真实的文件系统对话。也许它更适合这个功能,因为它除了与文件系统对话外并没有做太多事情。另见 "Stop Using Mocks (for a while)" by Harry Percival