2012-01-24

Grab bag of tips when working with App Engine unit testing

[update: App Engine doc bug is being fixed and should be publicly visible in 1.6.2 or 1.6.3]

First, an announcement: register for PyCon! It's a great conference and tons of fun. Early bird ends on the 25th.

With that out of the way, this blog post is going to be testing an App Engine app. This can be, liking testing any complex system, is painful. And unfortunately the documentation on this subject is somewhat lacking for App Engine. But hopefully this post about random things to be aware of when writing unit tests for App Engine can help prevent someone else from having to learn the hard way like I did.

First thing you should do when testing an App Engine app is set up a testbed. This will allow you to stub out various parts of App Engine so your tests run in isolation without having stale state carrying over. You basically create a testbed which holds state. You then activate the system by setting some core environment variables with defaults. After that you do your own tweaks to env vars. From there you activate various stubs. And then finally you deactivate the whole thing to clean up...

But you need to realize there is an apparent error in the docs (which will be fixed no later than 1.6.3, possibly 1.6.2). The stated order of calling activate() and setup_env() are wrong; you should call activate() before calling setup_env() in order to set the default env vars before you begin to set your own.

Another thing you should know about is that setup_env() has an overwrite argument. This tells the method to overwrite any pre-existing env var; otherwise the call will skip changing any pre-existing env vars. This can call issues if your app presets some env vars that should be overridden during testing. I would honestly consider using that argument every time to make sure things work the way you expect.


And the last tip specific to setup_env()is make sure to pass all of your env var changes in a single call. Otherwise you can end up with some settings be changed as they have a default value.

In relation to handling users, to specify who the logged in user is you should set the USER_EMAIL env var. That will cause App Engine to return a user with the specified email address when calling users.get_current_user().

Random thing about email testing is that mail.send_mail_to_admins() does not write to the mail stub to verify any email was sent. This is because while testing there is no way to specify who admins are, so there is no way to know who to say the emails are sent to. So in order to test the function you will need to mock out the call yourself (personally I'm a fan of mock if you don't already have a preferred mocking library).

And finally, you can mock our the request and response objects for a handler. You can call a handler's initialize() method with the object to use -- for the request and response, respectively -- which allows you to mock and test how the handler does things.

Bonus for those of you using the Python 2.7 runtime: I would consider using unittest's discovery features to drive your unit tests. You can start from my driver code and add what you need. But it's nice to just be able to plop down a new test module and have it automatically picked up.