Как вы издеваетесь над сервисом пользователя в App Engine? - PullRequest
21 голосов
/ 28 мая 2011

Я использую фреймворк Google App Engine 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)

Проблема в том, что я не нашел способа сообщить фальшивому пользовательскому сервису аутентификацию фальшивого пользователя. Итак, запустив этот тест, я (как и ожидалось) получаю

AssertionError: None == None

означает, что фальшивый пользовательский сервис сообщает моему приложению, что текущий пользователь не вошел в систему. Как я могу сказать фальшивому пользовательскому сервису, что он притворяется, что пользователь вошел в систему? В идеале я хотел бы иметь возможность указать псевдоним пользователя, адрес электронной почты, user_id и указать, являются ли они администратором. Кажется, что это было бы довольно распространенной вещью (так как вам нужно проверить, как приложение ведет себя, когда а) никто не вошел в систему, б) пользователь вошел в систему, и в) администратор вошел в систему), но поиск в Google "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 ]

17 голосов
/ 30 мая 2011

Ну, я не думаю, что есть официальный способ сделать это, но я читал исходный код и нашел "хакерский" способ сделать это, который до сих пор работает хорошо.(Обычно я беспокоюсь об использовании недокументированного поведения, но это набор тестов, поэтому он имеет значение, только если он работает на сервере dev.)

Сервер dev определяет текущего пользователя, вошедшего в систему, на основе трехпеременные среды:

  • USER_EMAIL: адрес электронной почты пользователя, и псевдоним пользователя.
  • USER_ID: уникальный идентификатор пользователя Google (строка).
  • USER_IS_ADMIN: «0», если пользователь не является администратором, «1», если пользователь является администратором.

Вы можете использовать os.environ, чтобы установить их так же, как в любой другой средепеременные, и они вступают в силу немедленно (, очевидно, это не будет работать на производственном сервере ).Но вы можете использовать их с 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)
11 голосов
/ 22 апреля 2013

Вот что сработало для меня, чтобы имитировать вошедшего в систему пользователя:

self.testbed.setup_env(USER_EMAIL='usermail@gmail.com',USER_ID='1', USER_IS_ADMIN='0')
self.testbed.init_user_stub()
10 голосов
/ 15 января 2014

В дополнение к ответу Биджан :

Фактическая проверка в google.appengine.api.users выглядит следующим образом:

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

Таким образом, ключ для установкипеременная окружения от USER_IS_ADMIN до '1'.Это можно сделать несколькими способами, но учтите, что вы изменяете глобальную переменную, и, таким образом, это может повлиять на другой код.Ключ должен сделать надлежащую очистку.

Можно использовать библиотеку Mock от до патча os.environ, использовать Testbed илисвернуть свой творческий путь.Я предпочитаю использовать Testbed, поскольку это намекает на то, что хак связан с appengineМакет не включен в версии Python до 3.3, поэтому это добавляет дополнительную тестовую зависимость.

Дополнительное примечание: При использовании unittest модуля я предпочитаю использовать 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
0 голосов
/ 02 февраля 2016

Вот пара вспомогательных функций, которые я создал для своих тестов на основе ответов здесь. Я вставил их в 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())
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...