I am processing a sequence of user-defined objects. It looks similar to the following:
class Thing(object):
def __init__(self, x, y):
self.x = x
self.y = y
The method I am currently testing has functionality similar to the following:
def my_function(things):
x_calc = calculate_something(t.x for t in things)
y_calc = calculate_something(t.y for t in things)
return x_calc / y_calc
The problem I am facing is testing the calls to calculate_something
. I want to assert that these calls happened, something like so:
calculateSomethingMock.assert_any_call(the_sequence)
I don't care about the order of the sequence passed into calculate_something
, but I do care that the elements are all present. I could wrap the generator function in a call to set
, but I don't feel like my test should be dictating what type of sequence is passed into calculate_something
. I should be able to pass it any kind of sequence. I could alternatively create a method that generates the sequence instead of using generator syntax and mock that method, but that seems like overkill.
How can I best structure this assertion, or is my trouble testing here an indication of poorly structured code?
I am using Python 2.7.3 with Mock 1.0.1.
(For anyone who feels compelled to comment on it, I'm aware I'm doing test last and that this isn't considered the greatest practice.)
Edit:
After watch this marvelous talk entitled "Why You Don't Get Mock Objects by Gregory Moeck", I have reconsidered whether I should even be mocking the calculate_something
method.
Looking at the Mock documentation, there is a call_args_list that will do what you want.
So you will Mock out calculate_something
on your test.
calculate_something = Mock(return_value=None)
After you my_function
has finished you can check the arguments passed by doing:
calculate_something.call_args_list
which will return a list of all the calls made to it (with the corresponding elements passed).
Edit:
(Sorry it took me so long, I had to install Python3.3 on my machine)
mymodule.py
class Thing:
...
def calculate_something:
...
def my_function(things):
# Create the list outside in order to avoid a generator object
# from being passed to the Mock object.
xs = [t.x for t in things]
x_calc = calculate_something(xs)
ys = [t.y for t in things]
y_calc = calculate_something(ys)
return True
test_file.py
import unittest
from unittest.mock import patch, call
import mymodule
class TestFoo(unittest.TestCase):
# You can patch calculate_something here or
# do so inside the test body with
# mymodule.calcualte_something = Mock()
@patch('mymodule.calculate_something')
def test_mock(self, mock_calculate):
things = [mymodule.Thing(3, 4), mymodule.Thing(7, 8)]
mymodule.my_function(things)
# call_args_list returns [call([3, 7]), call([4, 8])]
callresult = mock_calculate.call_args_list
# Create our own call() objects to compare against
xargs = call([3, 7])
yargs = call([4, 8])
self.assertEqual(callresult, [xargs, yargs])
# or
# we can extract the call() info
# http://www.voidspace.org.uk/python/mock/helpers.html#mock.call.call_list
xargs, _ = callresult[0]
yargs, _ = callresult[1]
xexpected = [3, 7]
yexpected = [4, 8]
self.assertEqual(xargs[0], xexpected)
self.assertEqual(yargs[0], yexpected)
if __name__ == '__main__':
unittest.main()
I haven't touched the code I was originally working with for a good while, but I've been reconsidering my approach to testing in general. I've been trying to be more careful about what I do and don't mock. I recently realized that I was unconsciously starting to following this rule of thumb: mock something if it makes my test shorter and simpler and leave it alone if it makes the test more complicated. Simple input/output testing suffices in the case of this method. There are no external dependencies like a database or files. So in short, I think the answer to my question is, "I shouldn't mock calculate_something
." Doing so makes my test harder to read and maintain.
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