2014-03-24 176 views
5

我有以下測試用例。請注意,下面的測試用例不是 試圖測試任何東西,只是試圖展示我遇到的掛起問題 。爲什麼我的Python單元測試線程掛起測試用例?

import http.server 
import urllib.request 
import threading 
import unittest 

class FooTest(unittest.TestCase): 
    def setUp(self): 
     print('---- setup start') 
     self.httpd = http.server.HTTPServer(('', 8080), http.server.SimpleHTTPRequestHandler) 
     thread = threading.Thread(target=self.httpd.serve_forever) 
     thread.start() 
     print('---- setup complete') 

    def tearDown(self): 
     print('---- teardown start') 
     self.httpd.shutdown() 
     print('---- teardown complete') 

    def test1(self): 
     print('---- test1 start') 
     print(threading.current_thread()) 
     urllib.request.urlopen('http://127.0.0.1:8080/foo') 
     print('---- test1 complete') 

    def test2(self): 
     print('---- test2 start') 
     print(threading.current_thread()) 
     urllib.request.urlopen('http://127.0.0.1:8080/foo') 
     print('---- test2 complete') 

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

我希望在嘗試執行這個測試用例時發生2個錯誤。相反, 測試用例會在以下輸出之後掛起。

C:\lab>python foo.py -v 
test1 (__main__.FooTest) ... ---- setup start 
---- setup complete 
---- test1 start 
<_MainThread(MainThread, started 12980)> 
127.0.0.1 - - [24/Mar/2014 21:53:57] code 404, message File not found 
127.0.0.1 - - [24/Mar/2014 21:53:57] "GET /foo HTTP/1.1" 404 - 
---- teardown start 
---- teardown complete 
ERROR 
test2 (__main__.FooTest) ... ---- setup start 
---- setup complete 
---- test2 start 
<_MainThread(MainThread, started 12980)> 

如果我從代碼中刪除test2,那麼我期待只有1個錯誤,並確保足夠 我看到它。

C:\lab>python foo.py -v 
test1 (__main__.FooTest) ... ---- setup start 
---- setup complete 
---- test1 start 
<_MainThread(MainThread, started 15720)> 
127.0.0.1 - - [24/Mar/2014 21:55:12] code 404, message File not found 
127.0.0.1 - - [24/Mar/2014 21:55:12] "GET /foo HTTP/1.1" 404 - 
---- teardown start 
---- teardown complete 
ERROR 

====================================================================== 
ERROR: test1 (__main__.FooTest) 
---------------------------------------------------------------------- 
Traceback (most recent call last): 
    File "foo.py", line 22, in test1 
    urllib.request.urlopen('http://127.0.0.1:8080/foo') 
    File "C:\Python34\lib\urllib\request.py", line 153, in urlopen 
    return opener.open(url, data, timeout) 
    File "C:\Python34\lib\urllib\request.py", line 461, in open 
    response = meth(req, response) 
    File "C:\Python34\lib\urllib\request.py", line 574, in http_response 
    'http', request, response, code, msg, hdrs) 
    File "C:\Python34\lib\urllib\request.py", line 499, in error 
    return self._call_chain(*args) 
    File "C:\Python34\lib\urllib\request.py", line 433, in _call_chain 
    result = func(*args) 
    File "C:\Python34\lib\urllib\request.py", line 582, in http_error_default 
    raise HTTPError(req.full_url, code, msg, hdrs, fp) 
urllib.error.HTTPError: HTTP Error 404: File not found 

---------------------------------------------------------------------- 
Ran 1 test in 0.032s 

FAILED (errors=1) 

爲什麼測試用例會掛起,如果有2個測試出錯?

回答

4

通話添加到server_close關閉套接字:

def setUp(self): 
    print('---- setup start') 
    handler = http.server.SimpleHTTPRequestHandler 
    self.httpd = http.server.HTTPServer(('', 8080), handler) 
    threading.Thread(target=self.serve).start() 
    print('---- setup complete') 

def serve(self): 
    try: 
     self.httpd.serve_forever() 
    finally: 
     self.httpd.server_close() 
+0

你的回答可以解決我的問題。謝謝。你能幫我理解爲什麼我的帖子中提到的問題不會出現在這個代碼中:https://gist.github.com/anonymous/9744045這似乎是在做一個平面模塊而不是單元測試測試案件? –

+0

每個測試都是'FooTest'的新實例(print'id(self)'以確認這一點),所以前面的'HTTPServer'不會被垃圾收集。在gist中更改代碼以使用'httpd1'和'httpd2',或者只保留對'sock = httpd.socket'的引用。這應該讓它掛起。 – eryksun

+1

謝謝。將變量更改爲'httpd1'和'httpd2'的確會導致代碼掛起。你知道爲什麼http://docs.python.org/3.4/library/socketserver.html沒有提到這個'server_close'方法嗎? –

2

我找到了另一種方式來在測試用例運行的httpd

import unittest 
import threading 
from http.server import HTTPServer, BaseHTTPRequestHandler 

class TestHttpRequests(unittest.TestCase): 
    @classmethod 
    def setUpClass(cls): 
     cls.httpd = HTTPServer(handler=BaseHTTPRequestHandler) 
     threading.Thread(target=cls.httpd.serve_forever).start() 

    @classmethod 
    def tearDownClass(cls): 
     cls.httpd.shutdown()