Python 如何在App Engine中模拟用户服务?

Python 如何在App Engine中模拟用户服务?,python,google-app-engine,Python,Google App Engine,我正在使用googleappenginetestbed框架编写模拟对象的测试用例。这是有案可查的。我使用模拟数据库(Testbed.init_datastore_v3_stub)使我的数据存储测试工作得很好,这使我的测试用例能够在一个快速、新的数据库上运行,并为每个测试用例重新初始化。现在我想测试依赖于当前用户的功能 还有另一个名为testbed.init_user_stub的测试床服务,我可以激活它来获得“假”用户服务。不幸的是,似乎没有关于这一点的任何文档。我正在这样激活和使用它: impo

我正在使用googleappengine
testbed
框架编写模拟对象的测试用例。这是有案可查的。我使用模拟数据库(
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)
def is_current_user_admin():
    return (os.environ.get('USER_IS_ADMIN', '0')) == '1'
问题是,我没有找到任何方法告诉“假”用户服务对“假”用户进行身份验证。所以运行那个测试,我(可以预见)得到

这意味着假用户服务告诉我的应用程序当前用户未登录。我如何告诉假用户服务假装用户已登录?理想情况下,我希望能够指定假用户的昵称、电子邮件、用户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()

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

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

  • USER_EMAIL:用户的电子邮件地址和昵称
  • 用户ID:用户唯一的Google ID(字符串)
  • 用户是管理员:如果用户是非管理员,则为“0”;如果用户是管理员,则为“1”
您可以使用
os.environ
像设置任何其他环境变量一样设置这些变量,它们会立即生效(显然这在生产服务器上不起作用)。但是您可以将它们与测试床的用户存根一起使用,当您停用测试床时,它们将被重置(您应该在
拆卸
时执行此操作,以便为每个测试用例提供一个新的环境)

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

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)

下面是我用来模拟登录用户的方法:

self.testbed.setup_env(USER_EMAIL='usermail@gmail.com',USER_ID='1', USER_IS_ADMIN='0')
self.testbed.init_user_stub()
除了您的回答之外:

实际签入的
google.appengine.api.users
如下所示:

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)
def is_current_user_admin():
    return (os.environ.get('USER_IS_ADMIN', '0')) == '1'
因此,关键是将环境变量
USER\u is\u ADMIN
设置为
'1'
。这可以通过多种方式完成,但请注意,您正在修改一个全局变量,因此这可能会影响其他代码。关键是要进行适当的清理

一个人可以用他们自己的方式来创造、使用或滚动。我更喜欢使用
Testbed
,因为它暗示黑客与appengine有关。3.3之前的Python版本中不包括Mock,因此这增加了一个额外的测试依赖项

额外注意:当使用时,我更喜欢使用而不是,因为失败时也会调用清理

示例测试:

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())
注意:
测试床。setup_env
应在测试床之后调用。激活
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
导入操作系统 >>>从google.appengine.ext导入测试床 >>> >>>tb=testbed.testbed() >>>tb.setup\u env(用户\u是\u admin='1') >>>tb.activate() >>>在os.environ中断言“USER\u IS\u ADMIN” >>>tb.deactivate() >>>断言“USER\u IS\u ADMIN”不在os.environ中 回溯(最近一次呼叫最后一次): 文件“”,第1行,在 断言错误
以下是我根据这里的答案为测试创建的几个帮助函数。我把它们放在一个
test\u 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)
示例用法(带):


这基本上是正确的想法。除了你想直接使用而不是
os.environ
。我对此投反对票,因为这会污染环境。需要注意的是,需要进行清理。这可以像@ryan所说的那样使用testbed来完成。这是可行的,但它似乎是testbed。setup_env()必须在testbed.activate()之前进行,否则它没有效果。@gargc很抱歉,您所说的是错误的,并且可能是“危险的”。请看我的答案进行演示。@siebz0r好电话!顺便说一句,我刚刚发现为什么我必须改变通话顺序才能让它正常工作。作为默认环境的一部分激活时,用户\u电子邮件和用户\u ID设置为空字符串(用户\u为\u admin时不会发生这种情况)。解决方案是将
overwrite=True
传递给setup\u env,否则环境中已有的任何变量都会导致get\u current\u user()始终为空。这需要为我提供
overwrite
参数,如中所述。