2015-02-24 88 views
4

我仍然無法使鼻子測試正常運行。 從命令行啓動時,dev_appserver運行良好,但是當我嘗試從functional_tests.py啓動時,它失敗。爲什麼我在setUp()中創建的實體無法通過nosetests訪問?

我在setUp()中創建了一個Client實體,但它不能從測試中訪問。

如何在測試中啓動dev_appserver時進入pdb調試器?
我試圖把pdb()斷點放在代碼中。代碼停止執行時,我無法進入調試器。我甚至不確定如何看到輸出。

$ nosetests

INFO  2015-02-24 19:08:56,172 devappserver2.py:726] Skipping SDK update check.  
INFO  2015-02-24 19:08:56,242 api_server.py:172] Starting API server at: http://localhost:62049  
INFO  2015-02-24 19:08:56,247 dispatcher.py:186] Starting module "default" running at: http://localhost:8080  
INFO  2015-02-24 19:08:56,249 admin_server.py:118] Starting admin server at: http://localhost:8000  
ERROR 2015-02-24 19:09:00,307 webapp2.py:1552] 'NoneType' object has no attribute 'key'  
Traceback (most recent call last):  
    File "/Users/Bryan/Desktop/GoogleAppEngineLauncher.app/Contents/Resources/GoogleAppEngine-default.bundle/Contents/Resources/google_appengine/lib/webapp2-2.5.2/webapp2.py", line 1535, in __call__  
    rv = self.handle_exception(request, response, e)  
    File "/Users/Bryan/Desktop/GoogleAppEngineLauncher.app/Contents/Resources/GoogleAppEngine-default.bundle/Contents/Resources/google_appengine/lib/webapp2-2.5.2/webapp2.py", line 1529, in __call__  
    rv = self.router.dispatch(request, response)  
    File "/Users/Bryan/Desktop/GoogleAppEngineLauncher.app/Contents/Resources/GoogleAppEngine-default.bundle/Contents/Resources/google_appengine/lib/webapp2-2.5.2/webapp2.py", line 1278, in default_dispatcher  
    return route.handler_adapter(request, response)  
    File "/Users/Bryan/Desktop/GoogleAppEngineLauncher.app/Contents/Resources/GoogleAppEngine-default.bundle/Contents/Resources/google_appengine/lib/webapp2-2.5.2/webapp2.py", line 1102, in __call__  
    return handler.dispatch()  
    File "/Users/Bryan/work/GoogleAppEngine/dermalfillersecrets/main.py", line 18, in dispatch  
    webapp2.RequestHandler.dispatch(self)  
    File "/Users/Bryan/Desktop/GoogleAppEngineLauncher.app/Contents/Resources/GoogleAppEngine-default.bundle/Contents/Resources/google_appengine/lib/webapp2-2.5.2/webapp2.py", line 572, in dispatch  
    return self.handle_exception(e, self.app.debug)  
    File "/Users/Bryan/Desktop/GoogleAppEngineLauncher.app/Contents/Resources/GoogleAppEngine-default.bundle/Contents/Resources/google_appengine/lib/webapp2-2.5.2/webapp2.py", line 570, in dispatch  
    return method(*args, **kwargs)  
    File "/Users/Bryan/work/GoogleAppEngine/dermalfillersecrets/main.py", line 95, in get  
    self.session['client'] = client.key.urlsafe()  
AttributeError: 'NoneType' object has no attribute 'key'  
INFO  2015-02-24 19:09:00,314 module.py:737] default: "GET/HTTP/1.1" 500 2354  
INFO  2015-02-24 19:09:00,377 module.py:737] default: "GET /favicon.ico HTTP/1.1" 200 8348  
INFO  2015-02-24 19:09:00,381 module.py:737] default: "GET /favicon.ico HTTP/1.1" 304 -  
EINFO  2015-02-24 19:09:08,482 shutdown.py:45] Shutting down.  
INFO  2015-02-24 19:09:08,483 api_server.py:588] Applying all pending transactions and saving the datastore  

======================================================================  
ERROR: 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 88, in test_guest_can_submit_contact_info  
    self.browser.find_element_by_name('id_name').send_keys("Kallie Wheelock")  
    File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/selenium/webdriver/remote/webdriver.py", line 302, in find_element_by_name  
    return self.find_element(by=By.NAME, value=name)  
    File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/selenium/webdriver/remote/webdriver.py", line 662, in find_element  
    {'using': by, 'value': value})['value']  
    File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/selenium/webdriver/remote/webdriver.py", line 173, in execute  
    self.error_handler.check_response(response)  
    File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/selenium/webdriver/remote/errorhandler.py", line 166, in check_response  
    raise exception_class(message, screen, stacktrace)  
NoSuchElementException: Message: Unable to locate element: {"method":"name","selector":"id_name"}  
Stacktrace:  
    at FirefoxDriver.prototype.findElementInternal_ (file:///var/folders/mw/0y88j8_54bjc93d_lg3120qw0000gp/T/tmpSjWZ6W/extensions/[email protected]/components/driver-component.js:9641:26)  
    at fxdriver.Timer.prototype.setTimeout/<.notify (file:///var/folders/mw/0y88j8_54bjc93d_lg3120qw0000gp/T/tmpSjWZ6W/extensions/[email protected]/components/driver-component.js:548:5)  

這裏是functional_tests.py代碼

import sys, os, subprocess, time, unittest, shlex  
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")  

from selenium import webdriver  
from google.appengine.api import memcache, apiproxy_stub, apiproxy_stub_map  
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):  
     # Start the dev server   
     cmd = "/usr/local/bin/dev_appserver.py /Users/Bryan/work/GoogleAppEngine/dermalfillersecrets/app.yaml --port 8080 --storage_path /tmp/datastore --clear_datastore --skip_sdk_update_check"  
     self.dev_appserver = subprocess.Popen(shlex.split(cmd),  
               stdout=subprocess.PIPE)  
     time.sleep(2) # Important, let dev_appserver start up  

     self.testbed = testbed.Testbed() 
     self.testbed.setup_env(app_id="dev~myapp")  
     self.testbed.activate()  
     #self.testbed.setup_env(app_id='dermalfillersecrets')  
     self.testbed.init_user_stub()  
     # Create a consistency policy with a probability of 1, 
     # the datastore should be available. 
     self.policy = datastore_stub_util.PseudoRandomHRConsistencyPolicy(probability=1) 
     # Initialize the datastore stub with this policy. 
     self.testbed.init_datastore_v3_stub(datastore_file="/tmp/datastore/datastore.db", use_sqlite=True, consistency_policy=self.policy)  
     self.testbed.init_memcache_stub()  
     self.datastore_stub = apiproxy_stub_map.apiproxy.GetStub('datastore_v3')  

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

     # setup client to make sure  
     from main import Client  
     if not (Client.query(Client.name == "Bryan Wheelock").get()):  
      logging.info("create Admin")  
      client = Client(  
      email = "[email protected]",  
      name = "Bryan Wheelock",  
      street1 = "555 Main St",  
      street2 = "unit 1",  
      city = "Atlanta",  
      zipcode = 99999,  
      phone = "(888)555-1212"  
      ).put() 
      # this sleep is to allow eventual consistency to propogate  
      time.sleep(2) 

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

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

    def test_guest_can_submit_contact_info(self):  
     from main import Client, Customer   
     client = Client.query(Client.name == "Bryan Wheelock").get()  
     orig_customer_count = Customer.query(ancestor=client.key).count()  
     self.browser.get('http://localhost:8080')  
     time.sleep(5)  
     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()  
     # the time delay is to allow eventual consisenency to happen.  
     time.sleep(4)  
     assert(Customer.query(Customer.name == "Kallie Wheelock").get())  
     # this should return 1 more record  
     final_customer_count = Customer.query(ancestor=client.key).count()  
     self.assertNotEqual(orig_customer_count, final_customer_count)  

     # Delete the Customer record  
     Customer.query(Customer.name =="Kallie Wheelock").delete() 

下面是main.py代碼:

import os 
import urllib 
import logging 

from google.appengine.api import users 
from google.appengine.ext import ndb 

import jinja2 
import webapp2 
from webapp2_extras import sessions 

class BaseHandler(webapp2.RequestHandler): 
    def dispatch(self): 
     self.session_store = sessions.get_store(request=self.request) 

     try: 
      # dispatch the request 
      webapp2.RequestHandler.dispatch(self) 
     finally: 
      # save all sessions 
      self.session_store.save_sessions(self.response) 

    @webapp2.cached_property 
    def session(self): 
     # Returns a session using the default cookie key. 
     return self.session_store.get_session() 

JINJA_ENVIRONMENT = jinja2.Environment( 
    loader = jinja2.FileSystemLoader(os.path.dirname(__file__)), 
    extensions=['jinja2.ext.autoescape'], 
    autoescape=True) 

DEFAULT_LEADBOOK_NAME = 'whatsmyname' 

def leadbook_key(leadbook_name=DEFAULT_LEADBOOK_NAME): 
    """Constructs a Datastore key for a LeadBook entity with leadbook_name.""" 
    return ndb.Key('LeadBook', leadbook_name) 

class Client(ndb.Model): 
    email = ndb.StringProperty() 
    name = ndb.StringProperty(indexed=True) 
    street1 = ndb.StringProperty() 
    street2 = ndb.StringProperty() 
    city = ndb.StringProperty() 
    zipcode = ndb.IntegerProperty() 
    phone = ndb.StringProperty() 
    signup = ndb.DateTimeProperty(auto_now_add=True) 

# this just creates a Client to use 
if not (Client.query(Client.name == "Bryan Wheelock").get()): 
    client = Client( 
    email = "[email protected]", 
    name = "Bryan Wheelock", 
    street1 = "555 Main St", 
    street2 = "unit 1", 
    city = "Atlanta", 
    zipcode = 99999, 
    phone = "(888)555-1212" 
    ).put() 

class Customer(ndb.Model): 
    # I commented out client property because using Ancestor Query(limited to 1 write per second) 
    #client = ndb.KeyProperty(kind=Client) 
    #email = ndb.StringProperty(indexed=True) 
    name = ndb.StringProperty(indexed=True) 
    street1 = ndb.StringProperty() 
    street2 = ndb.StringProperty() 
    city = ndb.StringProperty() 
    zipcode = ndb.IntegerProperty() 
    phone = ndb.StringProperty() 
    signup = ndb.DateTimeProperty(auto_now_add=True) 

class MainPage(BaseHandler): 
    def get(self): 
     leadbook_name = self.request.get('leadbook_name', 
              DEFAULT_LEADBOOK_NAME) 

     # This should be the Client record that shows the info of the owner of the local clinic 
     # the question is how do I get the site to show the correct Client? 
     client = Client.query(Client.name == "Bryan Wheelock").get() 
     self.session['client'] = client.key.urlsafe() 

     template_values = { 
      'client': client, 
      'leadbook_name': urllib.quote_plus(leadbook_name), 
     } 

     template = JINJA_ENVIRONMENT.get_template('index.html') 

     self.response.write(template.render(template_values)) 

class LeadBook(BaseHandler): 
    def post(self): 
     leadbook_name = self.request.get('leadbook_name', 
              DEFAULT_LEADBOOK_NAME) 

     client = ndb.Key(urlsafe=self.session['client']).get() 

     customer = Customer(parent = client.key) 
     customer.name = self.request.get('id_name') 
     customer.street1 = self.request.get('id_street') 
     customer.phone = self.request.get('id_phone') 
     customer.zipcode = int(self.request.get('id_zip')) 
     # show original number of customer to show the code works 
     starting_customer_count = Customer.query(ancestor=client.key).count() 
     #import pdb; pdb.set_trace() 
     customer.put() 
     # This should return the record 
     assert(Customer.query(Customer.name == "Kallie Wheelock").get()) 
     final_customer_count = Customer.query(ancestor=client.key).count() 
     #import pdb; pdb.set_trace() 
     query_params = {'leadbook_name': leadbook_name} 
     self.redirect('/?' + urllib.urlencode(query_params)) 

config = {} 
config['webapp2_extras.sessions'] = { 
    'secret_key': 'my-super-secret-key', 
} 
application = webapp2.WSGIApplication([ 
    ('/', MainPage), 
    ('/sign', LeadBook), 
], config = config, 
    debug=True) 
+1

我有類似的問題,並得到我不明白的各種錯誤。我懷疑應用程序引擎代碼中有一些奇怪的事情與手動設置測試平臺和/或dev_appserver.py的數據存儲有關。 – 2015-02-24 23:42:14

回答

4

的問題是在通過你的暗示評論:

# this sleep is to allow eventual consistency to propogate 

這實際上並非如此。最終的一致性與時間無關,它在本地數據存儲中模擬的方式更少;在測試中,數據存儲測試臺實現了一種策略,即初始讀取幾乎總是失敗。該文件解釋瞭如何在您的測試中使用tweak the policy;一個捷徑 - 再一次,只能在測試中使用,而不是在生產中 - 是在保存之後執行一個明確的.get(),這將始終使實體可見。

+0

我添加了datastore_stub_util.PseudoRandomHRConsistencyPolicy(概率= 1),但我的查詢仍然會返回一個值。 – BryanWheelock 2015-02-25 23:09:33

+0

我不確定你是什麼意思的顯式get()。在setUp中put()並沒有改變Client實體的可用性之後,我無法在文檔中找到它並立即創建get()。 – BryanWheelock 2015-02-25 23:24:22

相關問題