Python 如何确认使用GAE'保存实体;最终的一致性是什么?

Python 如何确认使用GAE'保存实体;最终的一致性是什么?,python,google-app-engine,selenium-webdriver,google-cloud-datastore,nose-gae,Python,Google App Engine,Selenium Webdriver,Google Cloud Datastore,Nose Gae,我正在尝试创建测试,以验证我的实体是否保存在数据库中。 当我在post函数中设置断点时,我可以看到记录保存后客户数量发生了变化。 我读 据我所知,由于最终的一致性,测试失败了,解决这个问题的方法是更改伪随机HRConsistencyPolicy设置 policy = datastore_stub_util.PseudoRandomHRConsistencyPolicy(probability=1) 当我再次运行测试时,我得到了相同的错误 创建这些测试有什么不对 > /Users/Br

我正在尝试创建测试,以验证我的实体是否保存在数据库中。 当我在post函数中设置断点时,我可以看到记录保存后客户数量发生了变化。 我读

据我所知,由于最终的一致性,测试失败了,解决这个问题的方法是更改伪随机HRConsistencyPolicy设置

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()  
尝试使用以获得强一致性,而不是最终一致性。从文档中:

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

如果这不起作用,我将尝试的下一件事是不要重用
查询
对象,而是第二次创建一个新的查询


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

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


如果您的目标只是测试用于编写实体的代码,您可以在此测试中插入一个实体,并检查是否可以使用该实体的密钥检索该实体。

伪随机一致性策略在这方面对您没有帮助,因为您的selenium测试正在提交一个实时html表单以及服务器上发生的后续db更新,这超出了您的策略范围


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

强/最终一致性没有问题,但测试的设计是错误的。为什么您要自己在测试中处理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等的信息。 您仍然可以自己控制测试床,但最好使用nosegae_u173*对其进行配置。(请阅读插件文档中的相关内容)


我记得,即使您以编程方式填充HTML表单,它也会起作用,但它不再是单元测试。

没有任何关于使用祖先查询的内容,他问的是如何使伪随机HRConsistencyPolicy忽略最终的一致性。@DmitrySadovnychyi-不,这不是他的问题。用他自己的话来说,他将该策略作为“绕过”最终一致性问题的一种方法。我认为OP的目的是简单地测试其保存实体的代码是否有效,这可以通过简单地按键检索实体来实现。您建议的是测试伪随机一致性策略是否有效(即,如果概率设置为零,实体将不保存,但如果概率设置为1,实体将保存)。当然,如果可能,他应该通过键接收,但如果他只有一个使用非祖先查询的选项(并且他不关心强一致性)--忽略最终的一致性是一个不错的选择。我不确定如何从通过Selenium保存的实体接收密钥。我该怎么做呢?通过在测试中对自定义密钥进行猴子补丁来设置自定义密钥,但我认为这不会有帮助,因为它的一致性不强。另外还有一个问题,除非您需要测试前端javascript,否则使用webtest()可能比使用Selenium更好。Webtest更快更简单。我最终将测试javascript,这只是一个沙箱。我在assertNotEqual之前添加了10秒的等待时间,测试仍然失败导入时间;时间。睡眠(10)您可能需要向我们展示处理程序在那里发生的情况,它是否真的在那里创建新客户?等。我可以看到在测试期间填写的表格,我也看到它正在提交。请提供处理程序代码,包括路由。