Как я могу подтвердить, что объекты сохраняются с помощью возможной согласованности GAE?

Я пытаюсь создать тесты, чтобы убедиться, что мои объекты сохраняются в базе данных. Когда я ставлю точки останова в функции публикации, я вижу, что количество клиентов меняется после сохранения записи. Я прочитал https://cloud.google.com/appengine/docs/python/tools/localunittesting

Из того, что я понял, тесты проваливались из-за возможной согласованности и способа обойти это путем изменения настроек 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   

Это содержимое файла functions_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 ответа

PseudoRandomHRConsistencyPolicy вам здесь не помогают, потому что ваш тест на селен отправляет живую HTML-форму и последующее обновление базы данных происходит на сервере, который выходит за рамки вашей политики.

То, что вы тестируете здесь - это тестирование от конца до конца, а не сам по себе модульный тест Таким образом, ваш тест на селен должен позаботиться о сценарии реального мира и должен подождать в течение предварительно определенного периода времени, прежде чем сравнивать счетчики.

В строгой / возможной согласованности нет ничего плохого, но дизайн ваших тестов неправильный. Почему вы пытаетесь разобраться с 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_*. (об этом читайте в документации к плагину)

И, насколько я помню, это будет работать, даже если вы будете программно заполнять свою HTML-форму, но это уже не юнит-тесты.

Запросы в конечном итоге непротиворечивы (если не указан предок), но get операция всегда последовательна.

Если ваша цель состоит в том, чтобы просто протестировать код для написания сущности, вы можете вставить сущность в этот тест и проверить, можете ли вы извлечь эту сущность, используя ее ключ.

Попробуйте использовать запросы предков, чтобы получить строгую согласованность вместо возможной согласованности. Из документов:

Запросы предков позволяют выполнять строго согласованные запросы к хранилищу данных...

Если это не сработает, я бы попробовал не использовать query объект, но создать новый во второй раз.

Если это тоже не работает, я думаю, что что-то не так. Я не знаком с тестированием браузера, но я с большим успехом использовал веб-тестирование для тестирования веб-конечных точек и не испытывал проблем с согласованностью во время модульного тестирования.

Другие вопросы по тегам