4

我有一些像这样的功能:


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()

在 setUp() 我的测试停止,因为 s.handle_request() 等待请求。我怎样才能解决这个问题?在另一个线程中运行 s.handle_request()?或者也许还有另一种解决方案?

编辑:我想测试“func”功能,而不是“app_200_hello”

4

5 回答 5

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,它将是您可能不必启动的真实服务器- 停止这样。

于 2009-03-11T13:44:29.073 回答
5

使用多处理在单独的进程中启动服务器

在 setUp 中执行以下操作:

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() 放在那里,那么后续的服务器实例可能会遇到端口已经被使用的问题。

于 2011-07-03T17:49:28.313 回答
4

您的服务器必须是一个单独的进程。

你会想要开始它subprocess.Popen()

如果您使用的是 Python 2.6,则可以在拆卸期间终止子进程。

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

如果您不使用 Python 2.6,那么终止服务器可能会令人不快。

于 2009-03-11T13:59:16.963 回答
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()


这被称为“猴子补丁”,有些人对此不以为然,但是对于测试您的代码,它可以使生活变得更加轻松,因为您无需更改原始代码即可使其可测试。

于 2009-03-11T16:28:20.693 回答
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 析构函数中关闭它。

于 2009-03-11T14:58:23.467 回答