2013-06-03 2 views
9

У меня есть сложная проблема, с которой я не могу справиться. Я , в настоящее время пишу модульные тесты для пользовательского auth-backend django. В нашей системе у нас фактически есть два backend: один встроенный django backend и пользовательский бэкэнд, который отправляет запросы на Java-API , который возвращает информацию о пользователе в форме XML. Теперь я пишу блок , поэтому я не хочу отправлять запросы за пределами системы, например, , что я не пытаюсь протестировать Java API, поэтому мой вопрос: как я могу обойти это и высмеять побочные эффекты наиболее надежным способом.Чистое издевательствование удаленных серверов и API для Django Unittests

Функция Я проверяю что-то вроде этого, где настройки значения URL только базовый URL для сервера Java, который проверяет подлинность данных имя пользователя и пароль и возвращает XML, а стоимость услуг является лишь некоторые магия для создания запроса URL-адреса, его неважным для нас:

@staticmethod 
def get_info_from_api_with_un_pw(username, password, service=12345): 
    url = settings.AUTHENTICATE_URL_VIA_PASSWORD 
    if AUTH_FIELD == "username": 
     params = {"nick": username, "password": password} 
    elif AUTH_FIELD == "email": 
     params = {"email": username, "password": password} 
    params["service"] = service 
    encoded_params = urlencode([(k, smart_str(v, "latin1")) for k, v in params.items()]) 
    try: 
     # get the user's data from the api 
     xml = urlopen(url + encoded_params).read() 
     userinfo = dict((e.tag, smart_unicode(e.text, strings_only=True)) 
         for e in ET.fromstring(xml).getchildren()) 
     if "nil" in userinfo: 
      return userinfo 
     else: 
      return None 

Таким образом, мы получаем XML, разобрать его в Словаре и если ключ ноль присутствует , то мы можем вернуть Dict и продолжать счастливым и аутентифицированы. Очевидно, что одно решение просто найти способ хоть как-то переопределить или monkeypatch логика в переменной XML, я нашел этот ответ:

How can one mock/stub python module like urllib

Я пытался реализовать что-то подобное, но детали там очень отрывочно, и я не мог заставить работать.

Я также захватил ответ XML и поместить его в локальном файл в папке теста с целью найти способ использовать это в качестве притворного ответа, который передается в параметр URL-адрес функции теста, что-то вроде этого будет переопределить URL:

@override_settings(AUTHENTICATE_URL_VIA_PASSWORD=(os.path.join(os.path.dirname(__file__), "{0}".format("response.xml")))) 
def test_get_user_info_username(self): 
    self.backend = RemoteAuthBackend() 
    self.backend.get_info_from_api_with_un_pw("user", "pass") 

Но также необходимо принимать во внимание URL здания логики, что функция определяет, (т.е. «URL + encoded_params»). Опять же, я мог бы переименовать , чтобы файл ответов был таким же, как и конкатенированный URL-адрес, но это становится меньше, чем хороший юнит-тест для этой функции и более «чит», вся информация о просто становится все более и более хрупкое все время с этими решениями, и его действительно просто арматура в любом случае, чего я тоже хочу избежать, если вообще возможно .

Я также задавался вопросом, может ли быть способ обслуживать xml на сервере разработки django, а затем указывать на него функцию? Это похоже на более здравое решение, но многие поисковые запросы не дали мне никаких подсказок, если бы такое было возможно или целесообразно, и даже тогда я не думаю, что это было бы испытание на запуск за пределами среды разработки.

Таким образом, в идеале, мне нужно, чтобы иметь возможность как-то издеваться «сервер», чтобы занять место в Java API, в вызове функции, или каким-то образом служить некоторый XML полезной нагрузки, что функция может открыть в качестве URL , или monkeypatch функция от самого теста, или ...

Есть ли в библиотеке макета соответствующие инструменты для выполнения таких задач?

http://www.voidspace.org.uk/python/mock

Итак, есть две точек на этот вопрос 1) Я хотел бы, чтобы решить мою конкретной проблемы в чистом виде, и что более важно 2) каковы лучших практики для чисто написания блок Джанго -tests, когда вы являетесь в зависимости от данных, куки-файлов и т. д. для аутентификации пользователей с удаленного API, который находится за пределами вашего домена?

ответ

1

Библиотека макетов должна работать при правильном использовании. Я предпочитаю библиотеку minimock, и я написал небольшой тестовый тест базового блока (minimocktest), который помогает в этом.

Если вы хотите интегрировать эту TestCase с Django, чтобы проверить urllib вы можете сделать это следующим образом:

from minimocktest import MockTestCase 
from django.test import TestCase 
from django.test.client import Client 

class DjangoTestCase(TestCase, MockTestCase): 
    ''' 
    A TestCase class that combines minimocktest and django.test.TestCase 
    ''' 

    def _pre_setup(self): 
     MockTestCase.setUp(self) 
     TestCase._pre_setup(self) 
     # optional: shortcut client handle for quick testing 
     self.client = Client() 

    def _post_teardown(self): 
     TestCase._post_teardown(self) 
     MockTestCase.tearDown(self) 

Теперь вы можете использовать этот TestCase вместо того, чтобы использовать тестовый пример Django непосредственно:

class MySimpleTestCase(DjangoTestCase): 
    def setUp(self): 
     self.file = StringIO.StringIO('MiniMockTest') 
     self.file.close = self.Mock('file_close_function') 
    def test_urldump_dumpsContentProperly(self): 
     self.mock('urllib2.urlopen', returns=self.file) 
     self.assertEquals(urldump('http://pykler.github.com'), 'MiniMockTest') 
     self.assertSameTrace('\n'.join([ 
      "Called urllib2.urlopen('http://pykler.github.com')", 
      "Called file_close_function()", 
     ])) 
     urllib2.urlopen('anything') 
     self.mock('urllib2.urlopen', returns=self.file, tracker=None) 
     urllib2.urlopen('this is not tracked') 
     self.assertTrace("Called urllib2.urlopen('anything')") 
     self.assertTrace("Called urllib2.urlopen('this is mocked but not tracked')", includes=False) 
     self.assertSameTrace('\n'.join([ 
      "Called urllib2.urlopen('http://pykler.github.com')", 
      "Called file_close_function()", 
      "Called urllib2.urlopen('anything')", 
     ])) 
+0

В вашем случае вам нужно высмеять следующее: 'import models; self.mock ('models.urlopen') ', поскольку, кажется, вы импортировали его в свой файл модели' from urllib import urlopen'. – Pykler

+1

Ах, это здорово, похоже, что такая насмешка - это всего лишь билет. Огромное спасибо! – osman

0

Вот основа решения, которое я закончил для записи. В конце концов я использовал библиотеку Mock, а не Mockito, но идея такая же:

from mock import patch 

@override_settings(AUTHENTICATE_LOGIN_FIELD="username") 
@patch("mymodule.auth_backend.urlopen") 
def test_get_user_info_username(self, urlopen_override): 
    response = "file://" + os.path.join(os.path.dirname(__file__), "{0}".format("response.xml")) 
    # mock patch replaces API call 
    urlopen_override.return_value = urlopen(response) 
    # call the patched object 
    userinfo = RemoteAuthBackend.get_info_from_api_with_un_pw("user", "pass") 
    assert_equal(type(userinfo), dict) 
    assert_equal(userinfo["nick"], "user") 
    assert_equal(userinfo["pass"], "pass") 
Смежные вопросы