Reputation: 33249
I'm primarily a Ruby guy, but lately I've been working on a lot of Python stuff, in particular, App Engine code. In Ruby, I'd use automated continuous integration (autotest), code coverage tools (rcov), static analysis (reek), and mutation testing (heckle) in my development process, but I'm not sure how best to set up a similar development process for an App Engine environment. I'd also be interested in analogs to RSpec and Cucumber for Python that could work in App Engine.
Upvotes: 15
Views: 2515
Reputation: 4427
For mutation testing try mutmut, it's available at https://github.com/boxed/mutmut/
It's as simple as "pip install mutmut" then "mutmut run" if you use pytest.
Upvotes: 0
Reputation: 33249
It looks like another new option is Agar, which is based on google.appengine.ext.testbed, part of the App Engine SDK. See this blog post for details.
Upvotes: 0
Reputation: 73752
On my GAE project, I use:
I also prefer many of Rails's idioms. I broke my tests into unit, and functional using Python packages. You can run a subset of tests using --tests=unit
or --tests=functional
. It is all a bit more manual than Rails but at least I can unit test the hard stuff and make sure I never have regressions.
I also made a simple FunctionalTest
class to do much of the very common actions in Rails, such as assert_response
and assert_xpath
(similar to assert_select).
class FunctionalTest(Test):
def get(self, *args, **kw):
self.response = app.get(*args, **kw)
def post(self, *args, **kw):
self.response = app.post(*args, **kw)
def assert_response(self, expected):
pattern = str(expected) if re.search(r'^\d+$', expected) \
else (r'^\d+ %s' % expected)
assert re.search(pattern, self.response.status, re.IGNORECASE), \
'Response status was not "%s": %s' % (expected, self.response.status)
def assert_xpath(self, path, expected):
element = ElementTree.fromstring(self.response.body)
found_nodes = element.findall('.' + path)
if type(expected) is int:
assert_equal(expected, len(found_nodes))
elif type(expected) is str or type(expected) is unicode:
assert (True in [(node.text == expected) for node in found_nodes])
else:
raise Exception, "Unknown expected value: %r" % type(expected)
If you are doing lots of ListElement equality searches, definitely learn the --tests=foo
syntax because testing for matching elements within a list is very slow.
Sometimes I like to load the Rails console against my fixture data to see what's going on in the test environment (i.e. script/console test
). To do something similar with GAE, run dev_appserver.py with the parameter --datastore_path="$TMPDIR/nosegae.datastore"
(or possibly substitute /tmp
for $TMPDIR
.
Upvotes: 15
Reputation: 15834
You won't always find one to one equivalents of Ruby testing tools in Python, but there are some great testing tools in Python. Some of the tools that I've found useful include:
If you are using Django on App Engine, it includes several extensions to unittest that allow you simulate an HTTP client and database persistence.
There are a ton of other tools that I have not used (such as PySpec and Behaviour) that could also be helpful. I haven't seen any mutation testing tool in Python, but I bet there is one out there (I would love to learn what it is).
Happy testing!
Upvotes: 26
Reputation: 298
Haven't used App Engine, but my feeling for the most popular python testing tools is
--with-coverage
, which uses coverage to give you code coverage
reports.You'll probably want to reference this (not quite complete) list of Python Testing Tools.
For BDD, the field was thin last time I checked. Many of the true BDD tools were not usable with nose and/or too limiting in the syntax they required. You might have some luck with spec, which is a BDD-like nose plugin. Just found pyccuracy, which looks a lot like cucumber, but I haven't tried it.
For what its worth, I now just use nosetests -v
(the nose runner with
--verbose), which will use the first line of the docstring in the test runner
output. That is, given a test like:
class TestFoo(unittest.TestCase):
def testAnyNameHere(self):
""" Foo should be bar"""
foo = "bar"
self.assertEqual(foo, 'bar')
nosetests will give:
$ nosetests -v
Foo should be bar... ok
-----------------------------
Ran 1 tests in 0.002s
OK
Upvotes: 3