
I've been working on a project using Google App Engine (GAE) called "les Freres Jacques" That manipulates images of people's face onto the cover of this old french LP. Look for it to be out in a couple months. Anyway, My favorite way to start any project is by doing TDD. Unfortunately I'm new to python and GAE simultaneously so I had to do plenty of research to figure out how to unit test a GAE app. Most importantly for me was the ability to test my models that are based on google's datastore api. What follows is some information to get you started writing unit tests against a GAE model. First, a list of the tools you need to install.
- Nose is a tool for running your python unit tests.
- NoseGAE is a plugin for nose that bootstraps the GAE environment.
sudo easy_install nose
sudo easy_install nosegaeNow let's create a test to exercize a simple GAE model object. Here is a file called test_simple_model.py
import unittestThe nose tool we installed earlier gives us a program called nosetests to run. When you call it it looks through your project and runs all your tests. We should call it now with the google app engine switch.
from google.appengine.api.users import User
from test_example.simple_model import SimpleModel
class TestSimpleModel(unittest.TestCase):
def test_creation(self):
user = User(email = "test@foo.com")
model = SimpleModel(goo_user = user)
model.put()
fetched_model = SimpleModel.all().filter('goo_user =', user).fetch(1)[0]
self.assertEquals(fetched_model.goo_user, user)
nosetests --with-gaeWheee!! It is a lovely failing test.
Good. Now we need to write some code to get our test passing. Here is what I wrote in a file called simple_model.py.
======================================================================
ERROR: Failure: ImportError (No module named simple_model)
----------------------------------------------------------------------
Traceback (most recent call last):
...
from test_example.simple_model import SimpleModel
ImportError: No module named simple_model
----------------------------------------------------------------------
Ran 1 test in 0.002s
FAILED (errors=1)
from google.appengine.ext import dbNow when I run
class SimpleModel(db.Model):
goo_user = db.UserProperty()
nosetests --with-gaeI get the lovely
.and I am happy because I see how I can do TDD with GAE! Here is a list of references I used to figure this stuff out. Hope you find them useful. You can find the full source of this example here.
----------------------------------------------------------------------
Ran 1 test in 0.008s
OK
- Python Easy Install
- Python Unit Test documentation
- Python Mock Object Framework
- Python Nose
- Python NoseGAE
The datastore persists between tests which isn't usually what I want to happen. I submitted an issue on the nose-gae issue tracker. In the meantime here is a workaround to make sure the datastore is flushed between runs. Add this method to your test class and call it in your setUp method.
Update: 1/29/09
from google.appengine.api import apiproxy_stub_map
from google.appengine.api import datastore_file_stub
def clear_datastore(self):
# Use a fresh stub datastore.
apiproxy_stub_map.apiproxy = apiproxy_stub_map.APIProxyStubMap()
stub = datastore_file_stub.DatastoreFileStub('appid', '/dev/null', '/dev/null')
apiproxy_stub_map.apiproxy.RegisterStub('datastore_v3', stub)
Reading Dom's well researched and documented post on testing App Engine applications. I thought I better spruce up my own post by adding a citation. The code from the clear_datastore method above comes from this message posted on the google app engine google groups mailing list.
Update: 5/17/09
There is currently an issue with nosegae. See defect 18. There are patches that fix the issue posted there. I downloaded the source code, removed the subversion directories, patched the code and ran
easy_install .in the root directory of the code. That fixed the issue for me.
Update: 5/17/09
As of GAE SDK 1.2.1 the appid of your datastore stub must match your appID. Make sure your call to DatastoreFileStub uses your actual app id.
6 comments:
Dude!. Thanks for this post. I've been scratching my head for the last two hours trying to figure out why entities are persisting between tests.
Thanks!
Hi Josh,
Thanks for your post. It gave me materials to write an extended post. I even mentioned you're a TDD adopter ;)
A+, Dom
--
http://domderrien.blogspot.com/2009/01/automatic-testing-of-gae-applications.html
Why does nosetests not find my test?
$ nosetests
----------------------------------------------------------------------
Ran 0 tests in 0.000s
OK
$ cat unittests.py
import unittest
from google.appengine.api.users import User
from models import Poll, Option
class TestPoll(unittest.TestCase):
def test_creation(self):
poll = Poll()
poll.put()
self.assertTrue(poll.id() != 0)
option = Option()
option.poll = poll
option.text = "some option text"
option.put()
self.assertEquals(1, len(poll.get_options()))
self.assertEquals(0, len(poll.get_responses()))
Your test script file name should have a test_ prefix.
Bless you!
If only http://somethingaboutorange.com/mrl/projects/nose/0.11.1/finding_tests.html
had anything nearly as informative.
You may also be interested in GAE Testbed (http://gae-testbed.googlecode.com) which provides a few base test cases that simplify testing things like sending emails or adding things to the Task Queue.
Post a Comment