I wish to test the following method:
class MyClass(object): ... def my_method(self, args): ... # Code which transforms args into arg1, arg2, ..., argN self.A.other_method1(arg1, arg2) self.A.other_method2(arg1, arg2, arg3) self.A.other_method3(arg1) self.B.other_method4(arg1, arg2) self.B.other_method5(arg1, arg2, arg4) self.B.other_method6(arg1, arg2, arg5, arg6)
Obviously this is an artificial example. I have created this example to focus on what I believe are the essential points:
my_methodfirst does some straightforward work to transform the values it is given. I am not concerned with how to test this aspect.
my_methodthen calls a sequence of methods on other attributes
Bof the class. I am not concerned with how to test those other methods.
- The sequence of method calls mutates
my_methodis very much a non-pure function. I don't believe I can redesign to make the function pure, as the other methods all do I/O.
My current approach is to mock out
unittest.mock.MagicMock, and then assert that these methods were called in the required order, with the expected arguments. My test code therefore looks like this:
from unittest.mock import MagicMock, call class MyTest(unittest.TestCase): def test_MyClass_my_method(self): x = MyClass() # Instantiation details omitted x.A = MagicMock() x.B = MagicMock() x.my_method(0) result_A = x.A.method_calls result_B = x.B.method_calls expected_result_A = [call.other_method1(1, 2), call.other_method2(1, 2, 3), call.other_method2(1)] expected_result_B = [call.other_method4(1, 2), call.other_method5(1, 2, 4), call.other_method6(1, 2, 5, 6)] self.assertEquals(result_A, expected_result_A) self.assertEquals(result_B, expected_result_B)
This works. But I can't help feel I have simply rewritten
my_method - and, worse, tightly coupled the test to the inner working of the method.
Is there a better way to test this method? Is the method actually poorly designed?