Reputation: 12254
I'm trying to setup unittests for my App Engine tutorial example.
I have defined the user in self.testbed.setup_env but the data is not visible in the tested code. Somehow, despite this data not being rendered by the print statement, it's being saved as a structured property in the datastore and is not deleted in the TearDown().
The code works when I use dev_appserver.py and enter the data into the form manually. When I attempt to run the tests with nosetests --with-gae, the mock user data is not not available Since I'm attempting to debug a test, I can't use a debugger and I'm relying on print statements.
import sys, os, subprocess, time, unittest, shlex
sys.path.append("/usr/local/google_appengine")
sys.path.append('/usr/local/google_appengine/lib/')
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")
os.environ['APPLICATION_ID'] = 'myapp'
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from google.appengine.api import memcache, apiproxy_stub, apiproxy_stub_map
from google.appengine.ext import testbed
from google.appengine.datastore import datastore_stub_util
from google.appengine.tools.devappserver2 import devappserver2
from guestbook import Author, Greeting
from google.appengine.api import users
class NewVisitorTest(unittest.TestCase):
# enable the datastore stub
nosegae_datastore_v3 = True
nosegae_datastore_v3_kwargs = {
'datastore_file': '/tmp/nosegae.sqlite3',
'use_sqlite': True
}
def setUp(self):
# Start the dev server
cmd = "/usr/local/bin/dev_appserver.py /Users/Bryan/work/GoogleAppEngine/guestbook/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='guestbook')
self.testbed.activate()
self.datastore_stub = apiproxy_stub_map.apiproxy.GetStub('datastore_v3')
# setup the dev_appserver
APP_CONFIGS = ['app.yaml']
self.browser = webdriver.Firefox()
self.browser.implicitly_wait(3)
#
def tearDown(self):
self.testbed.deactivate()
self.browser.quit()
self.dev_appserver.terminate()
def loginUser(self, email="[email protected]", id='123456', is_admin=False):
self.testbed.setup_env(
user_email=email,
user_id=id,
user_is_admin='1' if is_admin else '0',
overwrite=True
)
self.testbed.init_user_stub()
def test_guest_can_submit_new_greeting_and_author(self):
self.browser.get('http://localhost:8080')
self.loginUser()
self.browser.find_element_by_name('content').send_keys("Test Driven Development is awesome!!!")
self.browser.find_element_by_id('submit_guestbook').submit()
# Can I access them via querys on the database?
assert("Test Driven Development is awesome!!!" in self.browser.page_source)
self.browser.get('http://localhost:8000/datastore')
assert(Greeting.query(Greeting.author.email=='[email protected]').get())
self.browser.get('http://localhost:8000/datastore')
# I'm opening the datastore to see that the entity is saved.
# The entity is saved but the Author property is empty.
time.sleep(10) # this gives me time to view the datastore in browser.
self.assertEqual(1, Greeting.query().count())
Here is questbook.py:
import os
import urllib
from google.appengine.api import users
from google.appengine.ext import ndb
import jinja2
import webapp2
JINJA_ENVIRONMENT = jinja2.Environment(
loader=jinja2.FileSystemLoader(os.path.dirname(__file__)),
extensions=['jinja2.ext.autoescape'],
autoescape=True)
DEFAULT_GUESTBOOK_NAME = 'default_guestbook'
# We set a parent key on the 'Greetings' to ensure that they are all
# in the same entity group. Queries across the single entity group
# will be consistent. However, the write rate should be limited to
# ~1/second.
def guestbook_key(guestbook_name=DEFAULT_GUESTBOOK_NAME):
"""Constructs a Datastore key for a Guestbook entity.
We use guestbook_name as the key.
"""
return ndb.Key('Guestbook', guestbook_name)
class Author(ndb.Model):
"""Sub model for representing an author."""
identity = ndb.StringProperty(indexed=False)
email = ndb.StringProperty(indexed=True)
class Greeting(ndb.Model):
"""A main model for representing an individual Guestbook entry."""
author = ndb.StructuredProperty(Author)
content = ndb.StringProperty(indexed=False)
date = ndb.DateTimeProperty(auto_now_add=True)
class MainPage(webapp2.RequestHandler):
def get(self):
guestbook_name = self.request.get('guestbook_name',
DEFAULT_GUESTBOOK_NAME)
greetings_query = Greeting.query(
ancestor=guestbook_key(guestbook_name)).order(-Greeting.date)
greetings = greetings_query.fetch(10)
user = users.get_current_user()
if user:
url = users.create_logout_url(self.request.uri)
url_linktext = 'Logout'
else:
url = users.create_login_url(self.request.uri)
url_linktext = 'Login'
template_values = {
'user': user,
'greetings': greetings,
'guestbook_name': urllib.quote_plus(guestbook_name),
'url': url,
'url_linktext': url_linktext,
}
template = JINJA_ENVIRONMENT.get_template('index.html')
self.response.write(template.render(template_values))
class Guestbook(webapp2.RequestHandler):
def post(self):
guestbook_name = self.request.get('guestbook_name',
DEFAULT_GUESTBOOK_NAME)
greeting = Greeting(parent=guestbook_key(guestbook_name))
if users.get_current_user():
greeting.author = Author(
identity=users.get_current_user().user_id(),
email=users.get_current_user().email())
greeting.content = self.request.get('content')
######################################
import os
print("XXXXXXXXXXXXXXXXXXXXX guestbook in post() USER_EMAIL: %s" % os.environ.get('USER_EMAIL'))
#print("xoxoxoxoxoxoxoxoxoxox guestbook greeting.author.email: %s" % greeting.author.email )
greeting_key = greeting.put()
query_params = {'guestbook_name': guestbook_name}
self.redirect('/?' + urllib.urlencode(query_params))
application = webapp2.WSGIApplication([
('/', MainPage),
('/sign', Guestbook),
], debug=False)
Upvotes: 0
Views: 189
Reputation: 652
The datastore you can access from your test suite (the datastore_stub "mock" datastore) is not the same as the one the dev_appserver uses in your example. Hence changes to one of two datastores (such as adding an entity) will not be reflected in the other one, which is why your Greeting.query()
returns nothing.
Instead of running a full blown, separate dev_appserver process, you can write your tests using the webtest
framework (http://docs.pylonsproject.org/projects/webtest/en/latest/testapp.html) which will be simpler than having to manage a separate process; it will look this:
def setUp(self):
[...]
self.web_app = webtest.TestApp(guestbook.application)
[...]
def test_get(self):
response = self.web_app.get('/')
Upvotes: 0