Friday, May 17, 2013

REST App Response Status Code Testing Harness

When creating a web service that has different types of users with different privileges, it is useful to be able to quickly test whether the route that you just created returns the correct response code (e.g. 200, 401, 403). While you can obviously copy and paste a lot of code to make this work, I ended up creating a base class that does the job of initializing the database, getting the various kinds of users, and then running all of the tests at once. In essence, I wanted to be able to write a TestCase that looks something like this:
class SomeRouteTestCase(FlaskAppRouteStatusCodeTestCase):
    """Test for /foo"""

    __GET_STATUS_CODES__ = dict(
        user=200,
        admin=200,
        super_user=200
    )

    __PUT_STATUS_CODES__ = dict(
        user=403,
        admin=200,
        super_user=200
    )

    def get(self, user, test_data, db_data):
        self.login(user.email, user.password)
        rv = self.app.get('/foo')
        self.logout()
        return rv

    def post(self, user, test_data, db_data):
        self.login(user.email, user.password)
        rv = self.app.post(
            '/foo',
            data=json.dumps(dict(bar="barbar", bam="bambam")),
            content_type='application/json')
        self.logout()

    @unittest.skip("No PATCH")
    def test_patch(self):
        """Override the PATCH tester since /foo can't be patched."""
        pass
What kind of magic is this? Well, not any kind, really. A TestCase for a particular route simply defines the correct status codes for the various HTTP methods and the code to make the calls. In the example above, anyone should be able to perform GET /foo (all of them have a response code of 200), while only admins and super_users are allowed to POST. Since this route doesn't accept the PATCH method, we are telling unittest to skip it with the @unittest.skip decorator. Using this framework, one can test any route with all types of users with minimal effort.

In order to make this all work, we have to define the FlaskAppRouteStatusCodeTestCase that this TestCase inherits from. Fortunately for you, the basic structure of it is in the gist below and pretty straight forward. If you simply fill out the methods that initialize the database and get the set of users to perform the testing on, you can create tests for your routs with reckless abandon.

Although I have only added the methods in FlaskAppRouteStatusCodeTestCase for GET, POST, and PATCH, it should be trivial to add in any other methods for things like DELETE, PUT, etc. For your viewing pleasure, I've included the full example here:

No comments:

Post a Comment