Testing Google App Engine application

There was a time when I did no testing at all. (As The Kooks sing "You're so naive"). And Spycify was started and achieved it's first releases with about 0 tests. Those first versions were working, but had a whole bunch of annoying bugs. The only way I had is to do massive refactoring of whole app.

Previos versions were checked manually. I started a devserver, clicked through changed parts to see does it works. Sometimes this would not help and I had to upload app to production to test it there, manually, of course. Massive refactoring was a nice chance to start using automate testing. No more manual clicking and repeating myself.



At 42cc we practise TDD. I can't say I'm a big fan of it, though it has it's benefits. I decided that I would not write test for every single bit of code. Unittests for main parts of model and functional test or two for main workflow.

Installing


Testing of GAE apps needs a bit of tools. First we need nice way to run our tests. Plugin nose-gae for nosetest works great. To install it run in console:
pip install NoseGAE

To run tests:
nosetests -v --with-gae

Unittesting


It is done with plain unittest and mock modules. As I was not going to test a connection to face.com, which I am using for actual detecting faces, I saved json output for one of photos. In test I put this json into a model and run a method, that parses json, rejects recognitions with low level of convinience (if face.com is only 50% sure it is face, it is better to leave it alone without putting anything on it. Such results often get shirts, other wierd things) finds coordinates for glasses and moustaches. No actual working with datastore involved.

So, basically, my test only loads input and tests everything is present, checked and understood right in a parsed json.

This tests were not a big deal, actually. But they helped me to find lots of syntax errors, wrong imports and other bugs. As I was working on Spycify on weekends and never started a dev_server with it for about a month. So unittests helped me to find out that my model is generally speaking is working.

Functional tests


First functional test was meant to test that photo can be uploaded, then it processed and made right output. As I'm not going to analyze resulting image - find that we don't end up on one of error pages. This took a little bit more to establish. GAE SDK doesn't have testing infrastructure, as a django. So you have to create your own WSGI application and operate with it. I googled to see how people are doing it.
This is a part of my test_http.py, that checks workflow of Spycify:
import os
import re

from webtest import TestApp
from spycify.main import application

from wsgiproxy.exactproxy import proxy_exact_request

SERVER_NAME = os.environ['SERVER_NAME']
SERVER_PORT = os.environ['SERVER_PORT']

app = TestApp(proxy_exact_request, extra_environ={
                                    'wsgi.scheme': 'http',
                                    'SERVER_NAME': SERVER_NAME,
                                    'SERVER_PORT': SERVER_PORT,
                                    'HTTP_HOST': '%s:%s' % (SERVER_NAME,SERVER_PORT)
                                  })

def test_index():
    response = app.get('/')
    response.mustcontain('Spycify',
                         '<form',
                        )

At first I was trying to run separate testing server on seperate port. But it I couldn't make it run on SERVER_NAME and SERVER_PORT I wanted. Maybe this is not the best decision, but I decided to run a real dev server, and proxy all requests to dev_server.

Testing file upload
I thought I will get in trouble with testing file uploading. But that works the best way I could expect. Webtest, that is used for functional testing, doesn't run javascript, so in test we have to implement all AJAX-requests manually. For example, as I told in previous post, you have to get new upload url for every file upload. Now it is implemented by making separate request, that returns an url for upload. That script inserts url into upload form's action method.
response = app.get('/upload_url')
url_pattern = re.compile(r'http\:\/\/\w+\:\d+(\/.*)')
upload_url = url_pattern.match(response.body).groups()[0]

filename = 'tests/ivan.jpg'
response = app.post(upload_url,
        {'img_type': 'photo'},
         upload_files=(('file', filename),
)) 

Here we at first making a request to get an url. Then we take last part of url (without server name and port). And then app.post, passing a filename will upload file to app. Easy as pie!

Next page needs some care too. To fit the rule "Show something to user as soon as possible" I'm doing all the picture stuff in background. When user hits "Upload" his photo uploads to the server where it gets a number. Right after user is redirected a page with that number where he sees 'Loading...' message. On this page request is made to a server, that tells him to start photo processing. When it is done a response from server contains image link and the rest of the page.

So for all of this to work we have to strip photo id to make all request, that javascript does, manually. And when all of them are done and we reach last response from server the only thing is left - to check response consist img tag in it.

That's basically all the testing I have at the moment. Next test will be added when new bugs will be found or new features implemented.

There's one problem in such setup. There's no database clearing after each test. My app is not really about data, so I can manage without such feature. But on other apps I'd like to find out the way to do it. There must be something already done for it.

And for the ending a video about testing techniques at Google App Engine. I haven't seen it yet, but going to :)

1 comment:

  1. However, electromechanical slot improvement ensured that the reels spun without any mechanical springs. This sport was very fashionable, leading to a surge in electronic gaming. The first actual slot machine based on modern requirements is the Liberty Bell. Also, he included a staggered stopping mechanism for more 토토사이트 excitement.

    ReplyDelete

Thanks for your comment!
Come back and check response later.