mai*_*nsi 4 python unit-testing monkeypatching mocking pytest
我目前正在编写一个与竹构建服务器交互的小库。测试是使用 pytest 完成的。我陷入了以下问题。我想测试一个运行直到满足某些状态的 while 循环。阅读 pytest 文档,我试图“模拟”/monkeypatch 状态,但它并没有真正起作用。我可能在这里做错了一些基本的错误:这是有问题的 while 循环:
# determine current status
running = self._is_a_build_running()
# turn on and off running powerplug while building
while running:
self.feedback.turn_off_success()
self.feedback.turn_on_running()
time.sleep(self.blinker_time)
self.feedback.turn_off_running()
self._update_builds_status()
running = self._is_a_build_running()
Run Code Online (Sandbox Code Playgroud)
所以我用 pytest 尝试的是为正面和负面创建一个固定装置,_is_a_build_running如下所示:
@pytest.fixture(scope='function')
def mock_is_a_build_running():
return False
Run Code Online (Sandbox Code Playgroud)
然后使用 ThreadPool 使用此测试方法(此处解释了如何从 python 中的线程获取返回值?),因为我还需要包含 while 循环的方法的结果。
def test_update_status_running(bamboopickups, monkeypatch,
mock_update_overall_data_positive,
mock_update_builds_status_positive,
mock_is_a_build_running):
monkeypatch.setattr('BambooPickup._update_overall_data', lambda x: mock_update_overall_data_positive)
monkeypatch.setattr('BambooPickup._update_builds_status', lambda x: mock_update_builds_status_positive)
pool = ThreadPool(processes=1)
async_result = pool.apply_async(bamboopickups.update_status())
monkeypatch.setattr('BambooPickup._update_overall_data', lambda x: mock_update_overall_data_positive)
monkeypatch.setattr('BambooPickup._is_a_build_running', lambda x: mock_is_a_build_running)
actual = async_result.get()
expected = True
assert actual == expected
Run Code Online (Sandbox Code Playgroud)
这可能很容易用 pytest-mock 完成,但到目前为止我只使用这里描述的首选方式:http : //pytest.org/latest/monkeypatch.html。
因此,在进一步深入研究之后,我找到了一个目前令我满意的解决方案。我想分享它,以防其他人遇到同样的问题。实际上它很简单,通过https://gist.github.com/daltonmatos/3280885 的一些帮助类,我想出了以下测试代码:
def test_update_status_running(bamboopickup, monkeypatch,
mock_update_overall_data_positive,
mock_update_builds_status_positive):
monkeypatch.setattr('pickups.bamboo.bamboopickup.BambooPickup._update_overall_data', lambda x: mock_update_overall_data_positive)
monkeypatch.setattr('pickups.bamboo.bamboopickup.BambooPickup._update_builds_status', lambda x: mock_update_builds_status_positive)
with mock.patch.object(bamboopickup, '_is_a_build_running') as mockfoo:
mockfoo.return_value = AlmostAlwaysTrue(2)
bamboopickup.update_status()
Run Code Online (Sandbox Code Playgroud)
和助手类:
class AlmostAlwaysTrue(object):
def __init__(self, total_iterations=1):
self.total_iterations = total_iterations
self.current_iteration = 0
def __nonzero__(self):
if self.current_iteration < self.total_iterations:
self.current_iteration += 1
return bool(1)
return bool(0)
# Python >= 3
def __bool__(self):
if self.current_iteration < self.total_iterations:
self.current_iteration += 1
return bool(1)
return bool(0)
Run Code Online (Sandbox Code Playgroud)
还可以修改它以在某个时候返回异常并再次检查。我让这个问题再开放一点,以防万一有人有更干净的解决方案(我确信)。