2013-04-11 1 views
15

Ich bin ein Python. An diesen Tagen fahre ich mich selbst, um einen vollständigeren Komponententest auf einem Kernmodul in meinem Projekt zu machen. Da wir Komponententests immer mit den Methoden 'assertEqual', 'assertTrue' usw. ausführen, erfordern diese Methoden alle einen Rückgabewert der zu testenden Funktion. Ich frage mich, wie man einen Einheitentest für eine Funktion ohne Rückgabewert durchführt .In Python, wie Unit-Test auf eine Funktion ohne Rückgabewert zu tun?

Ich möchte hier ein kleines Beispiel zeigen, wie man Funktion def foo (self, msg) in HelloTest testen?

class HelloTest(object): 
    def foo(self, msg): 
     MSG = msg.upper() 
     self.bar(MSG) 

    def bar(self, MSG): 
     print MSG 

Antwort

5

In diesem speziellen Fall würde ich mock Drucken, dann verwenden Sie das Mock in meiner Behauptung. In Python werden Sie die Mock package zum Mock verwenden.

+0

+1: Für den Zeiger auf "Mock". –

+2

Nun, wenn er irgendetwas unter Python 3 verwendet, dann ist es nicht so einfach, einen Ausdruck zu erstellen. Er könnte sys.stdout vortäuschen, aber er müsste die Bar ändern – aychedee

2

In Python 3 können Sie tell print where to print to:

print (* Objekte, sep =‘‘, end = '\ n', file = sys.stdout, bündig = False)

So ein optionales Argument hinzu:

def bar(self, MSG, file=sys.stdout): 
    print(MSG, file=file) 

Im normalen Sprachgebrauch, es zu stdout gedruckt werden, aber für Unit-Tests können Sie Ihre eigene Datei übergeben.

In Python 2 ist ein bisschen unordentlicher ist, aber Sie können redirect stdout to a StringIO buffer:

import StringIO 
import sys 

out = StringIO.StringIO() 
sys.stdout = out 

# run unit tests 

sys.stdout = sys.__stdout__ 

# check the contents of `out` 
+1

Gute Lösung für Unit-Test auf Funktion mit Ausgabe zu tun. Aber was mich am meisten interessiert ist, wie man 'def foo (self, msg)' testet, da nicht alle Funktionen etwas mit stdout machen – Yarkee

2

Ihr Code kann als unterhalb dessen tut gleiche Aufgabe wie oben angegeben werden:

class HelloTest(object): 

    def foo(self, msg): 
     self.msg = msg.upper() 
     self.bar() 

    def bar(self): 
     print self.msg 

Unit-Test ist:

from hello import HelloTest 
import unittest 

class TestFoo(unittest.TestCase): 
    def test_foo_case(self): 
     msg = "test" 
     ob = HelloTest() 
     ob.foo(msg) 
     expected = "TEST" 
     self.assertEqual(ob.msg, expected) 

if __name__ == '__main__': 
    unittest.main(exit=False) 
9

Als eine andere Antwort erwähnt, können Sie die Python-Mock-Bibliothek verwenden, um Aussagen über zu machen Anrufe an Funktionen/Methoden

from mock import patch 
from my_module import HelloTest 
import unittest 

class TestFoo(unittest.TestCase): 

    @patch('hello.HelloTest.bar') 
    def test_foo_case(self, mock_bar): 

     ht = HelloTest() 

     ht.foo("some string") 
     self.assertEqual(ob.msg, "SOME STRING") 
     self.assertTrue(mock_bar.called) 

Das die bar Methode auf HelloTest Patches aus und ersetzt es durch ein Mock-Objekt, die Anrufe dagegen aufzeichnet.

Mocking ist ein bisschen wie ein Kaninchenloch. Tun Sie es nur, wenn Sie unbedingt müssen, weil es Ihre Tests spröde macht. Sie werden zum Beispiel niemals eine API-Änderung für ein verspottetes Objekt bemerken.

2

Dank @Jordan ‚s Einführung, Code, den ich dies und denke, dass es eine praktikable Unit-Test für HelloTest.foo ist

from mock import Mock 
import unittest 


class HelloTestTestCase(unittest.TestCase): 
    def setUp(self): 
     self.hello_test = HelloTest() 

    def tearDown(self): 
     pass 

    def test_foo(self): 
     msg = 'hello' 
     expected_bar_arg = 'HELLO' 
     self.hello_test.bar = Mock() 

     self.hello_test.foo(msg) 
     self.hello_test.bar.assert_called_once_with(expected_bar_arg) 


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

Ich verstehe nicht ganz, warum jeder, dass foo Anrufe bar überprüfen will.

Foo hat einige Funktionen und diese Funktionalität muss getestet werden. Wenn foo Bar benutzt, um dies zu tun, sollte das nicht mein Anliegen sein.

Das gewünschte Ergebnis ist, dass nach foo(msg) aufgerufen wird, ist, dass msg.upper() an stdout gesendet wird.

Sie können redirect stdout to a string buffer und überprüfen, ob der Inhalt dieses Zeichenfolgenpuffers dem entspricht, was Sie erwarten.

Beispiel:

import sys 
import unittest 
from io import TextIOWrapper, BytesIO 

class TestScript(unittest.TestCase): 
    def setUp(self): 
     self._old_stdout = sys.stdout 
     sys.stdout = TextIOWrapper(BytesIO(), sys.stdout.encoding) 

    def _output(self): 
     self._stdout.seek(0) 
     return self._stdout.read() 

    def test_foo(self): 
     hello_test = HelloTest() 
     hello_test.foo("blub") 
     self.assertEqual(self._output(), "BLUB") 

    def tearDown(self): 
     sys.stdout = self._old_stdout 
     self._stdout.close() 

Sie können das auch tun für stdin (und schreiben zu stdin eine Eingabe zu verspotten) und Sie können TestIOWrapper Unterklasse, wenn Sie etwas brauchen besondere getan werden, wie Text Nicht-Unicode erlaubt gesendet werden an sys.stdout ohne sys.stdout.buffer (Python 2 vs. Python 3) zu verwenden. Es gibt ein Beispiel dafür in this SO answer. Wenn Sie (noch) nur Python 2 verwenden, ist die Verwendung von StringIO möglicherweise besser als die Verwendung des io-Moduls.

Verwandte Themen