如何使用GAE的最终一致性确认实体是否已保存?

时间:2015-02-11 20:07:40

标签: python google-app-engine selenium-webdriver google-cloud-datastore nose-gae

我尝试创建测试以验证我的实体是否已保存在数据库中。 当我在post函数中放置断点时,我可以看到保存记录后客户计数发生了变化。 我看了https://cloud.google.com/appengine/docs/python/tools/localunittesting#Python_Writing_High_Replication_Datastore_tests

根据我的理解,由于最终的一致性,测试失败了,解决这个问题的方法是改变PseudoRandomHRConsistencyPolicy设置。

policy = datastore_stub_util.PseudoRandomHRConsistencyPolicy(probability=1)  

当我再次运行测试时,我得到了同样的错误。

创建这些测试我做错了什么?

> /Users/Bryan/work/GoogleAppEngine/dermalfillersecrets/main.py(137)post()  
-> customer.put()  
(Pdb) l  
134             query = Customer.query()  
135             orig_customer_count = query.count()  
136             import pdb; pdb.set_trace()  
137  ->         customer.put()  
138             import pdb; pdb.set_trace()  
139             query_params = {'leadbook_name': leadbook_name}  
140             self.redirect('/?' + urllib.urlencode(query_params))  
141       
142     config = {}  
(Pdb) orig_customer_count  
5  
(Pdb) c  
> /Users/Bryan/work/GoogleAppEngine/dermalfillersecrets/main.py(139)post()  
-> query_params = {'leadbook_name': leadbook_name}  
(Pdb) l  
134             query = Customer.query()  
135             orig_customer_count = query.count()  
136             import pdb; pdb.set_trace()  
137             customer.put()  
138             import pdb; pdb.set_trace()  
139  ->         query_params = {'leadbook_name': leadbook_name}  
140             self.redirect('/?' + urllib.urlencode(query_params))  
141       
142     config = {}  
143     config['webapp2_extras.sessions'] = {  
144         'secret_key': 'my-super-secret-key',  
(Pdb) query.count()  
6  

实体也会显示在数据存储区查看器中。

但是,我的测试仍然失败。

$ nosetests --with-gae  
F  
======================================================================  
FAIL: test_guest_can_submit_contact_info (dermalfillersecrets.functional_tests.NewVisitorTest)  
----------------------------------------------------------------------  
Traceback (most recent call last):  
  File "/Users/Bryan/work/GoogleAppEngine/dermalfillersecrets/functional_tests.py", line 80, in test_guest_can_submit_contact_info  
    self.assertNotEqual(orig_custs, query.count())  
AssertionError: 0 == 0   

这是functional_tests.py文件内容:

import os, sys  
sys.path.append("/usr/local/google_appengine")  
sys.path.append("/usr/local/google_appengine/lib/yaml/lib")  
sys.path.append("/usr/local/google_appengine/lib/webapp2-2.5.2")  
sys.path.append("/usr/local/google_appengine/lib/django-1.5")  
sys.path.append("/usr/local/google_appengine/lib/cherrypy")  
sys.path.append("/usr/local/google_appengine/lib/concurrent")  
sys.path.append("/usr/local/google_appengine/lib/docker")  
sys.path.append("/usr/local/google_appengine/lib/requests")  
sys.path.append("/usr/local/google_appengine/lib/websocket")  
sys.path.append("/usr/local/google_appengine/lib/fancy_urllib")  
sys.path.append("/usr/local/google_appengine/lib/antlr3")  

import unittest  
from selenium import webdriver  
from google.appengine.api import memcache  
from google.appengine.ext import db  
from google.appengine.ext import testbed  
import dev_appserver    
from google.appengine.tools.devappserver2 import devappserver2  


class NewVisitorTest(unittest.TestCase):  

    def setUp(self):  
        self.testbed = testbed.Testbed()  
        self.testbed.activate()  
        #self.testbed.setup_env(app_id='dermalfillersecrets')  
        self.testbed.init_user_stub()  
        ####################################################
        # this sets testbed to imitate strong consistency 
        from google.appengine.datastore import datastore_stub_util
        policy = datastore_stub_util.PseudoRandomHRConsistencyPolicy(probability=1)
        self.testbed.init_datastore_v3_stub(consistency_policy=policy)
        self.testbed.init_memcache_stub() 
        ####################################################

        # setup the dev_appserver  
        APP_CONFIGS = ['app.yaml']  

        self.browser = webdriver.Firefox()  
        self.browser.implicitly_wait(3)  

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

    def test_guest_can_submit_contact_info(self):  
        from main import Customer  
        query = Customer.query()  
        orig_custs = query.count()  
        self.browser.get('http://localhost:8080')  
        self.browser.find_element_by_name('id_name').send_keys("Kallie Wheelock")  
        self.browser.find_element_by_name('id_street').send_keys("123 main st")  
        self.browser.find_element_by_name('id_phone').send_keys('(404)555-1212')  
        self.browser.find_element_by_name('id_zip').send_keys("30306")  
        self.browser.find_element_by_name('submit').submit()  
        # this should return 1 more record  
        #import pdb; pdb.set_trace()  
        query = Customer.query()   
        self.assertNotEqual(orig_custs, query.count())  
        assert(Customer.query(Customer.name == "Kallie Wheelock").get())  
        # Delete the Customer record  
        Customer.query(Customer.name =="Kallie Wheelock").delete()  

4 个答案:

答案 0 :(得分:5)

PseudoRandomHRConsistencyPolicy在这里没有帮助你,因为你的selenium测试正在提交一个实时的html表单,并且后续的数据库更新发生在你的策略范围之外的服务器上。

您在这里测试的是端到端测试而不是单元测试本身。因此,您的硒测试应该照顾现实世界的情况,并且应该在比较计数之前等待预定义的时间段。

答案 1 :(得分:2)

强/最终一致性没有任何问题,但测试的设计是错误的。为什么你要自己尝试在测试中处理devappserver?为什么你试图在测试结束时删除实体?每个测试应该彼此隔离,并从空数据存储区开始,并进行一些可能的初始化。

请使用最新版本的NoseGAE插件。这是关于强/最终一致性的两个简单测试:

import unittest
from google.appengine.ext import ndb
from google.appengine.datastore import datastore_stub_util


class Foo(ndb.Model):
  pass


class TestEventualConsistency(unittest.TestCase):
  nosegae_datastore_v3 = True
  nosegae_datastore_v3_kwargs = {
    'consistency_policy': datastore_stub_util.PseudoRandomHRConsistencyPolicy(
      probability=0)}

  def test_eventual_consistency(self):
    self.assertEqual(Foo.query().count(), 0)
    Foo().put()
    self.assertEqual(Foo.query().count(), 0)


class TestStrongConsistency(unittest.TestCase):
  nosegae_datastore_v3 = True
  nosegae_datastore_v3_kwargs = {
    'consistency_policy': datastore_stub_util.PseudoRandomHRConsistencyPolicy(
      probability=1)}

  def test_strong_consistency(self):
    self.assertEqual(Foo.query().count(), 0)
    Foo().put()
    self.assertEqual(Foo.query().count(), 1)

请注意,我没有关于GAE路径,dev_appserver等的任何信息。 你仍然可以自己控制testbed,但最好用nosegae_ *配置它。 (在插件文档中阅读此内容)

我记得,即使您以编程方式填写HTML表单,它也会有效,但它不再是单元测试了。

答案 2 :(得分:1)

尝试使用ancestor queries来获得强大的一致性,而不是最终的一致性。来自文档:

  

祖先查询允许您对数据存储区进行强一致性查询...

如果这不起作用,我接下来要做的就是重用query对象,但第二次创建一个新对象。

如果这也不起作用,我的猜测是其他错误。我不熟悉浏览器测试,但我使用webtest非常成功地测试了Web端点,并且在单元测试时没有出现任何一致性问题。

答案 3 :(得分:1)

查询最终是一致的(除非设置了祖先),但get操作始终是一致的。

如果您的目标是简单地测试用于编写实体的代码,则可以在此测试中插入实体并检查是否可以使用其密钥检索此实体。