你如何模拟App Engine中的用户服务?

时间:2011-05-28 03:01:03

标签: python google-app-engine

我正在使用Google App Engine testbed框架来编写带有模拟对象的测试用例。记录在案here。我已经使用模拟数据库(Testbed.init_datastore_v3_stub)很好地运行了数据存储区测试,这使我的测试用例可以在一个快速,新鲜的数据库上运行,并为每个测试用例重新初始化。现在我想测试依赖于当前用户的功能。

还有另一个名为Testbed.init_user_stub的测试平台服务,我可以激活该服务以获取“虚假”用户服务。不幸的是,这个似乎没有任何文档。我正在激活并使用它:

import unittest
from google.appengine.ext import testbed
from google.appengine.api import users

class MyTest(unittest.TestCase):
    def setUp(self):
        self.testbed = testbed.Testbed()
        self.testbed.activate()
        self.testbed.init_user_stub()

    def testUser(self):
        u = users.get_current_user()
        self.assertNotEqual(u, None)

问题是我没有找到任何方法告诉“假”用户服务来验证“假”用户。所以运行那个测试,我(可以预见)得到

AssertionError: None == None

意味着虚假用户服务正在告诉我的应用当前用户未登录。如何判断虚假用户服务假装用户已登录?理想情况下,我希望能够指定假用户的昵称,电子邮件,user_id以及他们是否是管理员。这似乎是一个非常普遍的想要(因为你需要测试应用程序在a)没有人登录时的行为,b)用户登录,和c)管理员登录),但谷歌搜索“init_user_stub”几乎没有返回任何内容。

注意:如果您想测试上述程序,则需要将其添加到顶部:

import sys
sys.path.append('/PATH/TO/APPENGINE/SDK')
import dev_appserver
dev_appserver.fix_sys_path()

这是在底部:

if __name__ == '__main__':
    unittest.main()

4 个答案:

答案 0 :(得分:17)

嗯,我认为没有正式的方法可以做到这一点,但我一直在阅读源代码,我找到了一种“黑客”方式来做到目前为止运作良好。 (通常我会担心使用未记录的行为,但它是一个测试套件,所以只有它在开发服务器上运行才有意义。)

开发服务器根据三个环境变量计算出当前登录的用户:

  • USER_EMAIL:用户的电子邮件地址,用户的昵称。
  • USER_ID:用户唯一的Google ID(字符串)。
  • USER_IS_ADMIN:如果用户不是管理员,则为“0”;如果用户是管理员,则为“1”。

您可以像使用任何其他环境变量一样使用os.environ来设置它们,它们会立即生效(显然这不会对生产服务器起作用)。但是您可以将它们与testbed的user_stub一起使用,当您停用测试平台时,它们将被重置(您应该在tearDown上执行此操作,这样您就可以为每个测试用例获得一个全新的环境。)

由于设置环境变量有点笨拙,我写了一些包装函数来打包它们:

import os

def setCurrentUser(email, user_id, is_admin=False):
    os.environ['USER_EMAIL'] = email or ''
    os.environ['USER_ID'] = user_id or ''
    os.environ['USER_IS_ADMIN'] = '1' if is_admin else '0'

def logoutCurrentUser():
    setCurrentUser(None, None)

答案 1 :(得分:11)

以下是模拟已登录用户的方法:

self.testbed.setup_env(USER_EMAIL='usermail@gmail.com',USER_ID='1', USER_IS_ADMIN='0')
self.testbed.init_user_stub()

答案 2 :(得分:10)

除了Bijan的回答:

google.appengine.api.users中的实际检查如下:

def is_current_user_admin():
    return (os.environ.get('USER_IS_ADMIN', '0')) == '1'

因此,关键是将环境变量USER_IS_ADMIN设置为'1'。这可以通过多种方式完成,但请注意您正在修改全局变量,因此这可能会影响其他代码。关键是要进行适当的清理。

可以使用Mock librarypatch os.environ,使用Testbed或使用自己的创作方式。我更喜欢使用Testbed,因为它暗示黑客是与appengine相关的。在3.3之前的Python版本中不包含Mock,因此这增加了额外的测试依赖性。

额外注意:使用unittest module时,我更喜欢使用addCleanup而不是tearDown,因为setUp失败时也会调用清理。

示例测试:

import unittest

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


class AdminTest(unittest.TestCase):
    def setUp(self):
        tb = testbed.Testbed()
        tb.activate()
        # ``setup_env`` takes care of the casing ;-)
        tb.setup_env(user_is_admin='1')
        self.addCleanup(tb.deactivate)

    def test_is_current_user_admin(self):
        self.assertTrue(users.is_current_user_admin())

注意: Testbed.setup_env 应在 Testbed.activate之后调用。 Testbed在激活时拍摄os.environ的快照,在停用时恢复该快照。如果在激活之前调用Testbed.setup_env,则会修改实际os.environ而不是临时实例,从而有效地污染环境。

这表现得应该如此:

>>> import os
>>> from google.appengine.ext import testbed
>>> 
>>> tb = testbed.Testbed()
>>> tb.activate()
>>> tb.setup_env(user_is_admin='1')
>>> assert 'USER_IS_ADMIN' in os.environ
>>> tb.deactivate()
>>> assert 'USER_IS_ADMIN' not in os.environ
>>> 

污染环境:

>>> import os
>>> from google.appengine.ext import testbed
>>> 
>>> tb = testbed.Testbed()
>>> tb.setup_env(user_is_admin='1')
>>> tb.activate()
>>> assert 'USER_IS_ADMIN' in os.environ
>>> tb.deactivate()
>>> assert 'USER_IS_ADMIN' not in os.environ
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AssertionError

答案 3 :(得分:0)

这是我根据这里的答案为我的测试创建的几个辅助函数。我把它们放在test_helper模块中:

# tests/test_helper.py
import hashlib

def mock_user(testbed, user_email='test@example.com', is_admin=False):
    user_id = hashlib.md5(user_email).hexdigest()
    is_admin = str(int(is_admin))

    testbed.setup_env(USER_EMAIL=user_email,
                      USER_ID=user_id,
                      USER_IS_ADMIN=is_admin,
                      overwrite=True)
    testbed.init_user_stub()

def mock_admin_user(testbed, user_email='admin@example.com'):
    mock_user(testbed, user_email, True)

示例用法(使用NoseGAE):

import unittest

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

from tests.test_helper import mock_user, mock_admin_user

class MockUserTest(unittest.TestCase):
    def setUp(self):
        self.testbed = testbed.Testbed()
        self.testbed.activate()
        self.testbed.init_datastore_v3_stub()
        self.testbed.init_memcache_stub()
        ndb.get_context().clear_cache()

    def tearDown(self):
        self.testbed.deactivate()

    def test_should_mock_user_login(self):
        self.assertIsNone(users.get_current_user())
        self.assertFalse(users.is_current_user_admin())

        mock_user(self.testbed)
        user = users.get_current_user()
        self.assertEqual(user.email(), 'test@example.com')
        self.assertFalse(users.is_current_user_admin())

        mock_admin_user(self.testbed)
        admin = users.get_current_user()
        self.assertEqual(admin.email(), 'admin@example.com')
        self.assertTrue(users.is_current_user_admin())