我怎么在Google App Engine上进行敏捷开发(GAE下的unit test较好实践)

阅读更多

GAE上面的Unittest总结: http://ihere.appspot.com/post/2465
我是个推崇敏捷开发 重构 TDD 小步迭代 快速交付的 开发者,本来一直都用java来开发 , 最近迷上了python, google app engine, django
一开始我就想python上面的敏捷开发应该是怎么样的, 那阵还真不太了解 碰了不少壁:-( 经过一段时间的总结在 现在慢慢找到python gae上面开发的套路了, 觉得这个应该是GAE上开发以及unittest的较好实践了:P  特此发文分享:-)

参考:unit-tests-for-google-app-engine-apps, gaeunit, appengine helper

首先我想直接通过django的test 来测试gae上的app, 众所周知 appengine的mode层与django完全不同 这样其实直接运行test是不可取的 通过查阅这篇文章unit-tests-for-google-app-engine-apps得知 :其实appengine 的开发sdk本身已经提供了很多api stub 来帮助我们写unit test.  其实主要的地方是在test的setup方法里面注册这些stub 以及设置必要的环境变量, 以我发的demo 做个讲解

# coding=UTF-8
import unittest,os,urllib
from django.test.client import Client
#import the stubs, i.e. the fake datastore, user and mail service and urlfetch
from google.appengine.api import apiproxy_stub_map
from google.appengine.api import datastore_file_stub
#from google.appengine.api import mail_stub
from google.appengine.api import urlfetch_stub
from google.appengine.api import user_service_stub
from google.appengine.api.memcache import memcache_stub

from django.utils.encoding import force_unicode,smart_str
from google.appengine.api import users
from django.test import TestCase

from google.appengine.ext.db.djangoforms import ModelForm
import unittest
from django.test.client import Client
from google.appengine.api import users
import logging
from models import *

#How I do gae test:-)
#1.
#using gaeunit: http://code.google.com/p/gaeunit/
#and need to fix a bug of Django : Ticket #5176
#http://code.djangoproject.com/ticket/5176

#2.
#using appengine_django_helper:
#http://code.google.com/intl/zh-CN/appengine/articles/appengine_helper_for_django.html
#http://appengineguy.com/2008/06/proper-unit-testing-of-app-enginedjango.html [GFW blocked]
#run from cmd: manage.py test upload
#Here i use the 2nd way:P
#enjoy
class FileTest(unittest.TestCase):
  
    def setUp(self):      
        # Start with a fresh api proxy.
        apiproxy_stub_map.apiproxy = apiproxy_stub_map.APIProxyStubMap()

        # Use a fresh stub datastore.
        # From this point on in the tests, all calls to the Data Store, such as get and put,
        # will be to the temporary, in-memory datastore stub.       
        stub = datastore_file_stub.DatastoreFileStub(u'myTemporaryDataStorage', '/dev/null', '/dev/null')
        apiproxy_stub_map.apiproxy.RegisterStub('datastore_v3', stub)

        # Use a fresh stub UserService.
        apiproxy_stub_map.apiproxy.RegisterStub('user', user_service_stub.UserServiceStub())
        os.environ['AUTH_DOMAIN'] = 'gmail.com'
        os.environ['USER_EMAIL'] = '[email protected]' # set to '' for no logged in user
        os.environ['SERVER_NAME'] = 'testserver'
        os.environ['SERVER_PORT'] = '80'
        os.environ['USER_IS_ADMIN'] = '1' #admin user 0 | 1
        os.environ['APPLICATION_ID'] = 'appId'
      
        # Use a fresh urlfetch stub.
        apiproxy_stub_map.apiproxy.RegisterStub('urlfetch', urlfetch_stub.URLFetchServiceStub())

        #User a memcache stub
        apiproxy_stub_map.apiproxy.RegisterStub('memcache', memcache_stub.MemcacheServiceStub())

        # Use a fresh mail stub. mail stub have a conflit with the other api by now in the real gae environment. so comment it.
#        apiproxy_stub_map.apiproxy.RegisterStub('mail', mail_stub.MailServiceStub())

        # Every test needs a client. using django test client
        self.client = Client()     

...

可以看到 蓝色部分是在注册gae的api stub (想深入研究 可以看看sdk 里面的dev_appserver.py 那里有具体的使用方法)
注意我使用了 Django的Test Client 来模拟Http请求  使用时候需要fix个bug 改django.zip里面的文件 如果使用我得demo 的话是已经改好的:-)

接下来是个简单的test:

    def test_add_file(self):
        file=open('upload/testtxt.txt')
      
        response = self.client.post('/album/', {'file': file})
        file.close()
        self.failUnlessEqual(response.status_code, 302)
      
        returnResponse = self.client.get('/album/')
        self.assertTrue('testtxt.txt' in returnResponse.content)
        userFile=UserFile.all().filter("name =","testtxt.txt").get()
        self.assertTrue(userFile is not None)

向'/album/' 发起post请求  上传一个文件  并对response进行验证.

def tearDown(self):
        #For that we are using a temporary datastore stub located in the memory,we don't have to clean up.
        pass

tearDown不需要做任何动作  因为最开始setup的时候 只是在内存中建立了一个临时datastore


这就是一个完整的 unit test 例子:-)

上面这个例子 在本地控制台 直接 manage test upload 可以直接运行 不需要启动gae的dev_appserver
但是通过一段时间的开发发现本地的sdk环境与gae的实际环境不完全相同.本机调试ok ,上传之后 ,可能在实际环境中还会有意料之外的bug:-(  有没有一种方式可以在实际的环境中进行测试呢? 老外已经看到这个了 并且有了解决方案 就是 : gaeunit    它由http ajax驱动test的执行, 大家可以看我页面sidebar左下 lab 里面有个run test 就是这个出于安全原因当然只有admin才可以执行. 下面说下怎么将django 本身的test与gae unit结合,并且不留重复code:-)
gaeunit 执行的时候会执行所有test文件夹下的test 我的demo 中test/test_upload.py
中只有一句话:
from upload.tests import FileTest

简单吧:P 而FileTest就是我们之前写的django unit test .这样集成之后这两种方式可以混用而且没有重复test code :-)
本地开发时候可以运行 manage test upload
而在gae 实际环境中又可以 直接在浏览器:http://yourhost/test
来执行gae unit test
每次看到这个都很高兴:-)

 

  • 大小: 24.5 KB
  • 大小: 37.5 KB
  • 查看图片附件

你可能感兴趣的:(敏捷开发,GAE,Google,Django,OS)