模拟对外部模块的调用函数
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
我有一个 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