简体   繁体   English

如何确认实体已通过GAE的最终一致性保存?

[英]How do I confirm entities are saved with GAE's Eventual Consistency?

I'm trying to create tests to verify that my entities are being saved in the database. 我正在尝试创建测试以验证我的实体是否已保存在数据库中。 When I put breakpoints in the post function, I can see that the customer count changes after the record is saved. 当我在post函数中设置断点时,可以看到保存记录后客户计数发生了变化。 I read https://cloud.google.com/appengine/docs/python/tools/localunittesting#Python_Writing_High_Replication_Datastore_tests 我阅读了https://cloud.google.com/appengine/docs/python/tools/localunittesting#Python_Writing_High_Replication_Datastore_tests

From what I understood, the tests were failing because of Eventual Consistency and the way to get around that was to change the PseudoRandomHRConsistencyPolicy settings. 据我了解,测试由于最终一致性而失败,而解决该问题的方法是更改​​PseudoRandomHRConsistencyPolicy设置。

policy = datastore_stub_util.PseudoRandomHRConsistencyPolicy(probability=1)  

And when I ran the test again I got the same error. 当我再次运行测试时,我遇到了同样的错误。

What am I doing wrong with creating these tests? 创建这些测试我做错了什么?

> /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  

The entities also show up in the Datastore Viewer. 这些实体也会显示在数据存储查看器中。

However, my test keeps failing. 但是,我的测试一直失败。

$ 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   

This is the functional_tests.py file contents: 这是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()  

The PseudoRandomHRConsistencyPolicy is not helping you here because your selenium test is submitting a live html form and the subsequent db update happening on the server which is outside scope of your policy. PseudoRandomHRConsistencyPolicy无法在此处为您提供帮助,因为您的硒测试正在提交实时HTML表单,并且随后的数据库更新在服务器上发生,这不在您的策略范围之内。

What you testing here is the end to end testing not the unit test per se. 您在这里测试的是端到端测试,而不是单元测试本身。 So your selenium test should take care of the real world scenario and should wait for a predefined period of time before comparing the counts. 因此,您的硒测试应考虑实际情况,并应在预定义的时间段内比较计数。

There's nothing wrong with strong/eventual consistency, but the design of your tests is wrong. 强大/最终的一致性并没有错,但是测试的设计是错误的。 Why you're trying to deal with devappserver in your tests by yourself? 为什么要在测试中尝试自己处理devappserver? Why you're trying to remove entities in the end of the test? 为什么要在测试结束时尝试删除实体? Each test should be isolated from each other and start from empty datastore with some possible initializations. 每个测试应相互隔离,并从空的数据存储开始,并进行一些可能的初始化。

Please, use the latest version of NoseGAE plugin. 请使用最新版本的NoseGAE插件。 Here's two simple tests about strong/eventual consistency: 这是有关强/最终一致性的两个简单测试:

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)

Notice that I don't have anything about GAE paths, dev_appserver, etc. You still can control testbed by yourself, but better configure it with nosegae_*. 请注意,我没有关于GAE路径,dev_appserver等的任何信息。您仍然可以自己控制测试平台,但最好使用nasegae_ *对其进行配置。 (read about this in plugin documentation) (有关此信息,请参阅插件文档)

And as I remember, it will work even if you will programmatically fill your HTML form, but its not unittests anymore though. 而且我记得,即使您以编程方式填写HTML表单,它也可以工作,但现在不再是单元测试了。

Try using ancestor queries to get strong consistency instead of eventual consistency. 尝试使用祖先查询来获得较强的一致性,而不是最终的一致性。 From the docs: 从文档:

Ancestor queries allow you to make strongly consistent queries to the datastore... 祖先查询使您可以对数据存储区进行高度一致的查询...

If this does not work, the next thing I would try is to not reuse the query object but create a new the second time. 如果这不起作用,那么我接下来要尝试的是重用query对象,而是第二次创建新对象。

If this does not work either, my guess is that something else is wrong. 如果这也不起作用,那么我猜想还有其他问题。 I am not familiar with browser test, but I have used webtest with great success for testing web endpoints and have not had any consistency issues while unit testing. 我对浏览器测试不熟悉,但是我已经成功使用webtest来测试Web端点,并且在单元测试中没有任何一致性问题。

Queries are eventually consistent (unless an ancestor is set), but a get operation is always consistent. 查询最终是一致的(除非设置了祖先),但是get操作始终是一致的。

If your objective is to simply test the code for writing an entity, you can insert an entity in this test and check if you can retrieve this entity using its key. 如果您的目标只是测试编写实体的代码,则可以在该测试中插入一个实体,并检查是否可以使用其键检索该实体。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM