针对 Python 2 的处理程序测试

针对 Python 的本地单元测试一文介绍了如何为您的应用运行单元测试。虽然单元测试非常适合分别测试代码的各个单元,但应用的运行离不开这些代码单元的集成 - 因此集成测试同样很重要。

对于 App Engine 应用,请求处理程序是关键的集成点。当 WSGI 应用将请求路由到正确的处理程序时,处理程序本身会处理请求数据并生成响应(详细了解请求处理程序)。请求处理程序是普通的 Python 对象(就像任何其他函数或类一样),这使得它们易于在自动化测试中使用。但是,由于 WSGI 应用会像 shell 一样封装这些处理程序,因此我们将在测试中使用类似的 shell。

WebTest

对于我们的测试,我们将使用 WebTest 框架。WebTest 是一个库,它为您提供了一个简单的界面来测试基于 WSGI 的应用,从而测试请求处理程序。为此,它会将 WSGI 应用封装在一个随后可用于测试的特殊测试应用中。借助 WebTest,您可以在没有完整 App Engine 环境的情况下与处理程序互动;您可以轻松发出请求并修改请求环境。响应也有一个便于测试的界面。您并不是必须使用 WebTest,但它确实可以让测试容易很多。

在开始之前,请在本地机器上或打算运行处理程序测试的任何位置安装 WebTest。http://webtest.pythonpaste.org/#installation 提供了说明

测试简单的“Hello World”处理程序

我们首先测试一个简单的“Hello World!”处理程序,该处理程序使用纯文本回复来响应用户请求。处理程序的响应是“Hello World!”,内容类型为“text/plain”:

import webapp2
import webtest

class HelloWorldHandler(webapp2.RequestHandler):
   def get(self):
       # Create the handler's response "Hello World!" in plain text.
       self.response.headers['Content-Type'] = 'text/plain'
       self.response.out.write('Hello World!')

接下来,创建测试用例并初始化使用您的处理程序的测试应用:

...
class AppTest(unittest.TestCase):
    def setUp(self):
        # Create a WSGI application.
        app = webapp2.WSGIApplication([('/', HelloWorldHandler)])
        # Wrap the app with WebTest’s TestApp.
        self.testapp = webtest.TestApp(app)

    # Test the handler.
    def testHelloWorldHandler(self):
        response = self.testapp.get('/')
        self.assertEqual(response.status_int, 200)
        self.assertEqual(response.normal_body, 'Hello World!')
        self.assertEqual(response.content_type, 'text/plain')

如您所见,WebTest 可让您通过简单的 get() 调用来发出 GET 请求(其他请求方法具有类似的方法)。返回值是一个响应对象,您可以使用该对象来测试状态代码、正文、内容类型等等。请查看 WebTest 首页,以了解您可以执行的所有操作的详细说明。

创建使用 App Engine 服务的处理程序测试

下面,我们来看看如何测试使用 App Engine 服务的处理程序。这意味着我们现在必须处理可能会影响我们测试的两个组件:处理程序和我们正在使用的服务。如针对 Python 的本地单元测试一文中所述,在测试中处理服务的最佳方法是使用 testbed

以下示例使用 Memcache,但原则上对于其他服务(如 Datastore 或任务队列)也是如此。

我们测试的处理程序会缓存给定的键和值。请注意,我们从求参数中解析这两个值。

from google.appengine.api import memcache
from google.appengine.ext import testbed
import webapp2
import webtest

class CacheHandler(webapp2.RequestHandler):
  def post(self):
    key = self.request.get('key')
    value = self.request.get('value')
    memcache.set(key, value)

和以前一样,首先在测试中创建一个应用并使用 WebTest 封装该应用。另外,还要激活 Testbed 实例并注意在测试后停用该实例。

...
class AppTest(unittest.TestCase):

  def setUp(self):
    app = webapp2.WSGIApplication([('/cache/', CacheHandler)])
    self.testapp = webtest.TestApp(app)
    self.testbed = testbed.Testbed()
    self.testbed.activate()

  def tearDown(self):
     self.testbed.deactivate()

  def testCacheHandler(self):
    # First define a key and value to be cached.
    key = 'answer'
    value = '42'
    self.testbed.init_memcache_stub()
    params = {'key': key, 'value': value}
    # Then pass those values to the handler.
    response = self.testapp.post('/cache/', params)
    # Finally verify that the passed-in values are actually stored in Memcache.
    self.assertEqual(value, memcache.get(key))

设置测试框架

如果您愿意,可以设置测试框架。针对使用 WebTest 的处理程序的测试,可以像针对 App Engine 的单元测试一样执行。唯一的区别是,您需要确保已安装 WebTest。