I am currently writing a little lib that interacts with a bamboo build server. Testing is done using pytest. I got stuck at the following problem. I want to test a while loop that runs till some status is satisfied. Reading the pytest doc, I tried to "mock" / monkeypatch the status, but it doesnt really work. I am probably doing something elementary wrong here: This is the while loop in question:
# 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()
so what I tried with pytest was creating a fixture for a positive and a negative _is_a_build_running
like this:
@pytest.fixture(scope='function')
def mock_is_a_build_running():
return False
and then using this test method using a ThreadPool (explained here how to get the return value from a thread in python?) because I also would need a result from the method containing the while loop.
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
This is probably easily done with pytest-mock, but so far I was only using the prefered way described here: http://pytest.org/latest/monkeypatch.html.
So, after some more digging into the matter, I found a solution which satisfies me for now. I want to share it, in case anyone else runs into the same problem. Actually it is quite simple, and with some helper class from https://gist.github.com/daltonmatos/3280885 I came up with the following testcode:
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()
and the helper class:
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)
One could also modify it to return a exception at some point and check agains that. I keep the question a bit longer open, in case anyone has a cleaner solution (which I am sure of).
I add a param running_times_for_test
(default is -1) for this kind of function, if running_times reaches 0, then I broke the infinite loop.If running_time equals -1(default value), do as usual.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With