[TIL] Mocking Chained Calls

[TIL] Mocking Chained Calls

At work last week, I was asked to write tests for code that reaches out to Twilio's verify service. Here is what that code looks like (from the Twilio docs):

client = Client(account_sid, auth_token)

verification = client.verify \
                     .v2 \
                     .services('VAXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX') \
                     .verifications \
                     .create(to='+15017122661', channel='sms')

I was able to use @mock.patch(module.Client)* to mock the client, but I tried a few different ways, unsuccessfully, to patch the return value (or raise an exception) of create() all the way down that chain. After a couple breaks and different incantations entered into Google, I finally stumbled upon what I needed in the docs for unittest.mock, mocking chained calls.

I threw in a bunch of .return_values into my function call, and thought I had solved it, but that unfortunately didn't work. What I realized is that you only need those after the function calls (ones with parenthesis), not the properties (like verifications, v2, etc.). So the final product looks like:

@mock.patch(your_module.Client)
def test_name(mocked_client):
    mocked_client.return_value.verify.v2.services.return_value.verifications.create.return_value = 'return value that I want'
    ...
    # call the function
    # assert some things

If you want to raise an exception:

from twilio.base.exceptions import TwilioRestException
...
@mock.patch(your_module.Client)
def test_name(mocked_client):
    mocked_client.return_value.verify.v2.services.return_value.verifications.create.side_effect = TwilioRestException("", "")
    ...
    # call the function
    # assert some things

*Don't forget to mock the Client in your module, not the library itself in case other tests need the client.