Unit Testing Your Twilio App Using Python’s Flask and Nose

DuckHuntIt happens every time.  You fire up your editor.  The Twilio app spits off your fingers, deftly wielding the TwiML primitives you know to build your voice or SMS functionality.  You save. You commit. You deploy.  You set the request URLs for your Twilio number.  You gingerly unlock your phone and confidently tap in the digits.  Then you hear it:

“We’re sorry.  An application error has occurred. Goodbye.”

Rage wells inside you familiar to the impulse hearing the dog laugh at you in Duck Hunt.  You open the Twilio App Monitor and find the culprit – a fat fingered typo on your Dial verb.

Cue the sad trombone.

It happens to all of us.  And when you’re iterating on your Twilio code, the wash-rinse-repeat cycle of changing code, deploying to a web server and manually testing on your phone can become tiresome quickly.

One technique I use to reduce this frustration is writing unit tests against my Twilio webhook endpoints as I work on my app.  Utilizing a web framework’s test client to write these tests quickly helps keep my focus on the app I’m trying to write and the knucklehead errors that produce the Duck Hunt experience to a minimum.

What You’ll Learn

In this post, we’ll show that technique in action with Flask, my go-to framework for writing Twilio apps in Python.

  • How to write a simple Twilio Conference endpoint with Flask
  • Write a unit test with Nose for that endpoint
  • Expand that unit test into a test case we can reuse for all our Twilio apps
  • Then show how that reusable test case can be applied to more complex flows.

What You’ll Need

Let’s Cut Some Code

To start, we’ll open a text editor in our Python environment with the Twilio and Flask modules installed and clack out a simple app that will create a Twilio Conference room using the verb and the noun.

Here’s a quick cut in a file we’ll name app.py:

Now Let’s Test It

I think this code might be right, but let’s make sure by writing a quick unit test. To do this, we’ll open another file called test_app.py. In that file, we’ll import our app and define a unit test using unittest in the Python standard library. We’ll then use the Flask test client to make a test request to the app and see if the app throws an error.

We then run the unit test using Nose By issuing the following command, Nose will go through our unit test file, find all TestCase objects and execute each method prefixed with test_:

nosetests -v test_app.py

Oh biscuits – looks like we got a bug.

D’oh. The name of the TwiML noun for conferencing isn’t “Conf” but “Conference.” Let’s revisit our app.py file and correct the bug.

Now with the Conference line corrected, we can rerun our tests with the same command as above:

Awesome. And we didn’t have to pick up our phones to figure out the error.

Now Let’s Make Sure This Code Does What We Want

Making sure the code doesn’t throw an error is a good first step, but we also want to make sure our Twilio app performs the way we intend. First we need to check that the app returns a response that Twilio can interpret, make sure it is creating a valid Dial verb and finally that the Dial points to the correct Conference room.

To help, we’ll use ElementTree, an XML parser from the Python standard library. This way we can interpret the TwiML response the same way Twilio would. Let’s look how we would add this to test_app.py:

Now run both tests using Nose:

Killer. Now we’re confident this app is doing what we want in addition to returning an appropriate response.

DRYing Up Our Tests For Reuse

It’s great that we know our new Twilio endpoint works without needing to test it manually, but Twilio apps rarely use a single webhook endpoint. As our application grows in complexity we can see these two tests are going to repeat a whole lot of code. Let’s see if we can refactor our tests into a generic test case we can use for any Twilio webhook endpoints we build in the future.

To do this, we’ll create a generic TwiMLTest class and leverage the built-in setUp() method to instantiate our Flask test client automatically with every test.

Great start – now let’s create a helper method that accepts a response and does the basic validation that it is working TwiML.

Finally, instead of creating a new POST request with every test, let’s create two more helper methods that will create Twilio requests for calls and messages that we can easily extend with custom parameters. Let’s add a new class to test_app.py.

Excellent – now we can refactor our original tests for the conference using the new helper methods, making the tests much shorter:

Perfect – let’s run our tests using Nose and see if we’re golden.

And all is good with the world.

Go Forth And Test

With our generic test case for Twilio apps, now writing tests is quick and simple. We wrote a quick Conferencing app, tested it using Nose, and then refactored those tests into a generic case you can use with all your apps. By using this test case, testing our Twilio apps built on Flask can be fast and painless, reducing both the amount of time spent manually testing with your phone and the number of times you have to hear the dreaded “Application Error” voice.

Because no likes to hear this on the other end of the phone.