2

My code is organized as several independent classes. Each dependency is injected by a setter, so in the end I create instances of classes B and C, set their parameters and inject them to class A.

Now I want to test class A in isolation, so my first intuition is to create mocks for B and C. My problem is that methods from B and C are called multiple times with different parameters and return different results. How can I mock that?

Pseudo-code:

b = B()
b.set_param1(value1)
b.set_param2(value2)
c = C()
c.set_param1(value1)
c.set_param2(value2)
a = A()
a.set_b(b)
a.set_c(c)

a.run():
    X = self.get_b().method1(x) # should return 1
    Y = self.get_b().method2(y) #s hould return 2
    Z = self.get_c().method1(z) # should return a complicated dataframe
    W = self.get_c().method2(w)# should return yet another complicated dataframe
    # process W X Y Z ...

Everything I read on mocking in python is limited to very simple examples where mocking can happen locally.

I know I can set a sequence of return values, but this is limited to a single method and is not future proof if the code logic changes.

Is it even possible or correct to test such a hierarchy?

  • why did you set_param1 twice with different values for class B and C? – Yu Zhang Jul 30 '18 at 22:27
  • Regarding is not future proof if the code logic changes, there is no such thing a future proof in my personal opinion. We will always need to go back to modify our old codes in some way to accommodate future changes. – Yu Zhang Jul 30 '18 at 22:32
  • @YuZhang thanks for pointing this out. I corrected the typo. By future proof, I meant that if I use this sequence feature, whenever A calls B methods in a different sequence I'll have to rework on B mock, when in fact functionality should be the same. Also if I have several successive calls with the same parameters, I'll have to copy the returned object multiple times. – Samuel Jul 31 '18 at 7:42
1

While there's not a lot of details, the unittest.mock library should handle this for you? I think this is what you mean when you say "I know I can set a sequence of return values, but this is limited to a single method"

As for future-proofing, if you're trying to test A in isolation, you're verifying that A works with the contracts to B and C not changing. If the interfaces to B and C change, tests there should catch that, and you'll need to update your mocks/tests in A.

  • Please see my comment to @YuZhang in original question. Also, how would I mock two methods of B? – Samuel Jul 31 '18 at 7:49
  • For mocking two methods of B, you just define them, something like mockB = B(); mockB.method1 = MagicMock(return_value=1); mockB.method2 = MagicMock(return_value=2)? Make sure to define the mock in the appropriate context. For a fancier solution, you could use patch(), which has notes about scope – ernie Jul 31 '18 at 17:35

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.