2009-03-11 3 views
3

У меня есть некоторые функции, как это:использованием wsgiref.simple_server в UnitTests

 

URL = 'http://localhost:8080' 
def func(): 
    response = urlopen(URL) 
    return process(response) 
 

И я хочу, чтобы проверить его с UnitTest.

я сделал что-то вроде этого:


from wsgiref.simple_server import make_server 
def app_200_hello(environ,start_response): 
    stdout = StringIO('Hello world') 
    start_response("200 OK", [('Content-Type','text/plain')]) 
    return [stdout.getvalue()] 

s = make_server('localhost', 8080, app_200_hello) 

class TestFunc(unittest.TestCase): 
    def setUp(self): 
     s.handle_request() 

    def test1(self): 
     r = func() 
     assert r, something 

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

В нАлАдкА() мои тесты остановить, потому что s.handle_request() ожидает запроса. Как я могу обойти это? Запустите s.handle_request() в другом потоке? или может быть другое решение ?

EDIT: Я хочу проверить функцию «Func», а не «app_200_hello»

ответ

7

Если вы тестируете приложение WSGI, я настоятельно рекомендую werkzeug.test который получает вокруг этих вопросов путем тестирования самого приложения без сервера :

from werkzeug.test import Client 

# then in your test case 
def test1(self): 
    client = Client(app_200_hello) 
    appiter, status, headers = client.open() 
    assert ''.join(appiter) == 'Hello World' 
    assert status == '200 OK' 

Этот подход просто устраняет необходимость в WSGI-сервере.

Конечно, если вы хотите запустить сервер, вам придется использовать отдельный поток или процесс, но тогда вы должны быть уверены, что потом остановите его. Тем не менее, мне кажется, что единственный раз, когда вы захотите протестировать с помощью реального сервера, - это тестирование интеграции продукции, и в этом случае ваш сервер не будет wsgiref, это будет настоящий сервер, который вам, вероятно, не придется запускать -Перед собой.

+0

Спасибо. Это похоже на то, что я хочу. Но werkzeug слишком велик. Является ли клиентский класс http://dev.pocoo.org/projects/werkzeug/browser/werkzeug/test.py#L551 независимым от другой части пакета werkzeug? –

+0

Нет, это не является независимым, но это не большая зависимость для развития. На самом деле это крошечный. –

+0

Ох. Я запутался. Я не хочу тестировать app_200_hello. Я хочу проверить «func» –

3

Ваш сервер должен быть отдельным процессом.

Вы хотите, чтобы запустить его с subprocess.Popen()

Если вы используете Python 2.6, вы можете убить подпроцесс во время разборки.

def setUp(self): 
    self.server= subprocess.Popen("python","myserver","etc.") 
def tearDown(self): 
    self.server.kill() 

Если вы не используете Python 2.6, то убийство сервера может быть неприятным.

+0

Я не думаю, что это хорошая идея. Что касается меня, то будет очень сложно для системы запускать/останавливать python после каждого теста. И я не хочу писать для тестов какое-то сложное приложение wsgi. Я хочу написать много простых приложений wsgi для многих ситуаций. –

+0

Поскольку tearDown останавливает сервер WSGI, Python заканчивается нормально. Очень легко для системы остановить Python. –

+0

wsgiref.simple_server должен быть запущен как отдельный процесс, который будет полезен. –

0

Мое решение:

 

URL = 'http://localhost:8085' 
def func(): 
    response = urlopen(URL) 
    return response.read() 

import unittest 
from wsgiref.simple_server import WSGIServer, WSGIRequestHandler 
import threading 
from urllib2 import urlopen 
from cStringIO import StringIO 

def app_200_hello(environ,start_response): 
    stdout = StringIO('Hello world') 
    start_response("200 OK", [('Content-Type','text/plain')]) 
    return [stdout.getvalue()] 

server = WSGIServer(('localhost', 8085), WSGIRequestHandler) 
server.set_app(app_200_hello) 

t = threading.Thread(target=server.serve_forever) 
t.start() 

class TestFunc(unittest.TestCase): 
    def setUp(self): 
     pass 

    def test1(self): 
     r = func() 
     self.assertEqual(r, 'Hello world') 

    def __del__(self): 
     server.shutdown() 

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

 

я начинаю "сервер" в другом потоке и выключение Это у TestFunc деструктора.

+0

Разве вы не беспокоиться о том, чтобы оставить сервер в плохом состоянии после каждого теста, и что влияет на тесты? Как бы вы тестировали файлы cookie, например. Если вам нужно это сделать, подумайте об использовании процессов и сделайте это по запросу. –

2

Вы также можете предоставить макетную версию urlopen, которая фактически не запускает сервер.

Предполагая, что ваш исходный код был в mycode.py, в тестовом коде вы могли бы сделать что-то вроде:



import mycode 

class TestFunc(unittest.TestCase): 
    def setUp(self): 
     # patch mycode, to use mock version of urlopen 
     self._original_urlopen = mycode.urlopen 
     mycode.urlopen=self.mock_urlopen 

    def tearDown(self): 
     # unpatch urlopen 
     mycode.urlopen=self._original_urlopen 

    def mock_urlopen(self,url): 
     # return whatever data you need urlopen to return 

    def test1(self): 
     r = func() 
     assert r, something 

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


Это известно как «обезьяна заплат», которые некоторые нахмурившись при, но для тестирования кода возможное сделать жизнь намного проще, так как вам не нужно менять исходный код, чтобы сделать его проверяемым.

4

Использование многопроцессорная для запуска сервера в отдельном процессе

в нАлАдкА сделать что-то вроде:

self.port = 8000 
server = make_server('', self.port, make_my_wsgi_ap()) 
self.server_process = multiprocessing.Process(target=server.serve_forever) 
self.server_process.start() 

затем в Teardown сделать:

self.server_process.terminate() 
self.server_process.join() 
del(self.server_process) 

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

+0

Это лучший способ сделать это IMO. – vangheem