a fun one from yesterday.
i'm working on some code which makes frequent use of this pattern:
Code:
with patch.object(Node, 'activate') as m_activate:
self.cluster.activate_node()
m_activate.assert_called_once()
(btw "with patch.object(...)" is a nifty construction.)
so i come along to add a feature. i write a test making use of this pattern and run it, expecting it to fail. once it does, i'll go fix cluster.activate_node() so that it calls activate() on the cluster's Node object. tdd bitches!
except the test doesn't fail. that's weird. i rewrite the assertion like this:
Code:
assert_equal(1, m_activate.call_count)
and this time it does fail. wat.
so i ask the guy who wrote this code if he knows what's going on. shouldn't these two assertions be identical? he agrees they should. we look at a few things before i discover that i cannot find a definition of assert_called_once(). the mock library provides an assert_called_once_with(), but no assert_called_once(). wtf? how is this working?
well, since m_activate is a mock, it has no problem at all executing m_activate.assert_called_once -- the method is simply mocked out! the original author never noticed this because he wrote all his tests post-hoc and
never verified that they could fail. he had other assertions that pinned down most of the behavior, so the code did what it was supposed to, but essentially he was lucky.
and that's why you should quit your bitching and learn tdd. at the very least, you need to make sure your tests are able to fail. we'll see if this lesson landed on my colleague.
once discovered, of course, the problem was easy to fix:
Code:
def assert_called_once(mock):
assert_equal(1, mock.call_count)
(i could have avoided some refactoring from foo.assert_called_once() to assert_called_once(foo) by adding my method directly to the mock library, but that felt a little too magical and hard to read.)