2009-03-11 5 views
3

J'ai quelques fonctions comme celui-ci:utilisant wsgiref.simple_server dans unittests

 

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

Et je veux le tester avec unittest.

je fait quelque chose comme ceci:


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

A setUp() mes tests arrêter parce que s.handle_request() attend la demande. Comment je peux contourner ça? Lancez s.handle_request() dans un autre thread? ou peut-être il y a une autre solution ?

EDIT: Je veux tester la fonction « FUNC », pas « app_200_hello »

Répondre

7

Si vous testez une application WSGI, je vous recommande fortement werkzeug.test qui obtient autour de ces questions en testant l'application elle-même sans serveur :

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' 

Cette approche supprime simplement le besoin d'un serveur WSGI tout à fait.

Bien sûr, si vous vouliez démarrer un serveur, vous deviez utiliser un thread séparé ou un processus, mais vous devez ensuite vous assurer de l'arrêter par la suite. Cependant, il me semble que la seule fois où vous voudriez tester avec un serveur réel est pour des tests d'intégration de production, auquel cas votre serveur ne sera pas wsgiref, ce sera un vrai serveur que vous n'aurez probablement pas à démarrer -arrête comme ça.

+0

Merci. Cela ressemble à ce que je veux. Mais werkzeug est trop gros. La classe client http://dev.pocoo.org/projects/werkzeug/browser/werkzeug/test.py#L551 est-elle indépendante d'une autre partie du package werkzeug? –

+0

Non, ce n'est pas indépendant, mais ce n'est pas une grosse dépendance pour le développement seulement. En fait, c'est minuscule. –

+0

Oh. Je me suis emmêlé. Je ne veux pas tester app_200_hello. Je veux tester "func" –

3

Votre serveur doit être un processus distinct.

Vous voulez commencer avec subprocess.Popen()

Si vous utilisez Python 2.6, vous pouvez tuer le sous-processus au cours tearDown. Si vous n'utilisez pas Python 2.6, il peut être désagréable de tuer le serveur si vous n'utilisez pas Python 2.6.

+0

Je ne pense pas que ce soit une bonne idée. Quant à moi, il sera très difficile pour le système de démarrer/arrêter Python après chaque test. Et je ne veux pas écrire pour tester une application wsgi complexe. Je veux écrire de nombreuses applications wsgi simples pour de nombreuses situations. –

+0

Puisque tearDown arrête le serveur WSGI, Python se termine normalement. Très facile pour le système d'arrêter Python. –

+0

wsgiref.simple_server doit être démarré en tant que processus séparé pour être utile. –

0

Ma solution:

 

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

 

je commence à "serveur" dans un autre thread et l'arrêt au moment TestFunc destructor.

+0

N'êtes-vous pas Vous vous inquiétez de laisser le serveur en mauvais état après chaque test, et cela affecte-t-il les tests? Comment testeriez-vous les cookies par exemple? Si vous devez le faire, pensez à utiliser des processus et à le faire par requête. –

2

Vous pouvez également fournir une version fictive de urlopen qui n'exécute pas réellement le serveur.

Si l'on suppose votre code d'origine était mycode.py, dans votre code de test que vous feriez quelque chose comme:



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


Ceci est connu comme « patching singe » dont certains de désapprouver, mais pour tester votre code, il peut Simplifiez-vous la vie, car vous n'avez pas besoin de changer votre code original pour le rendre testable.

4

Utilisez multitraitement pour démarrer le serveur dans un processus séparé

à setUp faire quelque chose comme:

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

puis à tearDown faire:

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

que j'ai trouvé que si vous ne mettez pas explicitement le del() là, alors les instances de serveur suivantes peuvent avoir un problème avec le port déjà utilisé.

+0

C'est la meilleure façon de le faire IMO. – vangheem

Questions connexes