Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

mock twilio rest client for testing

I am using the following code to send SMS.

 from twilio.rest import Client
 from django.conf import settings

 def send_sms(phone, content=generate_sms_code()):
    client = Client(settings.ACCOUNT_SID, settings.AUTH_TOKEN)
    return client.messages.create(
        to=phone,
        from_=settings.FROM_DEFAULT_NUMBER,
        body=content)

what is the proper way of mocking twilio api?

I am using twilio 6.5.2

like image 244
static Avatar asked Oct 20 '17 13:10

static


Video Answer


2 Answers

There are two approaches you can use here:

  1. Use fake twilio client and use to unit test your code
  2. Use real client with test account

For unit tests the approach 1 is usually a better idea, so, for example, you can run your tests offline and the tests are executed faster. Also you need to test your code, but with approach 2 you will also be testing twilio itself.

That brings another problem: how do we make sure that your fake client works close enough to the real client to make your tests useful? In the best case, twilio would release and maintain such test client, but it doesn't look like they have it.

What you can do is to combine the approaches: have the fake client (approach 1) and have some tests to verify that your fake client behaves same as real one.

But you can start with just the approach 1 - you need to build your test client (usually something super-simple) and make sure that it works close enough to the real one (maybe switch to the real client and make sure that your tests still work).

The danger here is that if there were some breaking API changes on twilio side, your tests would still be passing, although the real app will be broken. On the other hand, even if the tests were failing, the real app would still be broken, so you can't really protect your app in this scenario.

Here is how we could make a test client for your code:

class TwilioTestClient:

    def __init__(self, sid, token):
        self.sid = sid
        self.token = token
        self.messages = TwillioTestClientMessages()

class TwillioTestClientMessages:

    self.created = []

    def create(self, to, from_, body):
        self.created.append({
            'to': to,
            'from_': from_,
            'body': body
        })

And in your test you can then do something like this:

import twilio
# Replace real client with our test client
twilio.Client = TwilioTestClient

// invoke your code
send_sms(phone, content)

assert TwillioTestClientMessages.created == [{
    'to': phone,
    'from_': settings.DEFAULT_NUMBER,
    'body': content
})

This is a simplified approach, additionally you can use mock library to make the test client.

Or you can patch the real twilio client to only block actual HTTP requests and record the parameters / emulate the response. You will need to look into twilio client source code to find the method (or methods) to mock, here is similar approach I've used to mock facebook graph response by mocking the Session.request method of the requests library:

mock_facebook_data = {
    'id': 'test_id',
    'first_name': 'First Name',
    'last_name': 'Last Name'
}

def get_facebook_data(self, method, url, **kwargs):
    # Make sure we are mocking the expected request.
    assert method == 'get'
    assert url == (
        'https://graph.facebook.com/me?fields=first_name,id,'
        'last_name&access_token={}'.format(facebook_access_token)
    )

    class ResponseMock:

        def __init__(self, data):
            self.data = data
            self.status_code = 200

        def json(self):
            return self.data

    return ResponseMock(mock_facebook_data)

monkeypatch.setattr('requests.sessions.Session.request', get_facebook_data)

Now, when the app tries to fetch the data from facebook API, it will get the static data instead and that data will be wrapped into the ResponseMock object, minimal representation of what we would get from the real request.

Also check this article: Unit Testing Your Twilio App Using Python’s Flask and Nose. As I understand, they are using real client for unit tests, but they also mention the other side of the integration - how to test your code that is being invoked from the twilio side. In this case you also need to emulate the Twilio server and there is an example of such emulation in the article.

like image 177
Boris Serebrov Avatar answered Sep 22 '22 00:09

Boris Serebrov


If you want to test SMS functionality of twilio using apis, you can create a free twilio account to test it.

But I would recommend using environment specific values for the account SID, auth token etc. You would want to seperate your production environment from testing/development environment, so the same piece of code runs over both the environment with different twilio accounts.

As suggested by @txfun in the comments, refer twilio Free account link

like image 20
cyberrspiritt Avatar answered Sep 26 '22 00:09

cyberrspiritt