2013-04-11 5 views
15

Jestem pythonerem. W tych dniach prowadzę się, aby wykonać bardziej kompletny test jednostkowy na jakimś rdzeniowym module w moim projekcie. Ponieważ zawsze wykonujemy test jednostkowy metodami "assertEqual", "assertTrue" i tak dalej, wszystkie te metody wymagają wartości zwracanej z testowanej funkcji, zastanawiam się, jak wykonać prosty test jednostkowy dla pewnej funkcji bez wartości zwracanej .W python, jak zrobić test jednostkowy na funkcji bez wartości zwracanej?

Chciałbym pokazać mały przykład tutaj, jak przetestować funkcję def foo (self, msg) w HelloTest?

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

    def bar(self, MSG): 
     print MSG 

Odpowiedz

5

W tym konkretnym przypadku chciałbym udawać druk, a następnie użyć makiety w moim twierdzeniu.

W języku Python użyjesz Mock package do fałszywek.

+0

+1: Dla wskaźnika "makiety". –

+2

Cóż, jeśli używa czegoś poniżej Pythona 3, to nie jest to takie proste. Mógł kpić z sys.stdout, ale musiałby zmienić pasek. – aychedee

2

W Pythonie 3, można tell print where to print to:

print (* obiekty, wrzesień =”” end = '\ n', file = sys.stdout spłukać = False)

Więc dodać opcjonalny argument:

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

W normalnych warunkach użytkowania, będzie wydrukować na standardowe wyjście, ale dla testów jednostkowych można przekazać swój własny plik.

W Pythonie 2 to trochę Messiera, ale można 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

Dobre rozwiązanie do testowania jednostki na funkcji z wyjściem. Ale najbardziej zależy mi na testowaniu 'def foo (self, msg)', ponieważ nie wszystkie funkcje robią coś ze stdoutem – Yarkee

2

kod może być jak podano poniżej której robi to samo zadanie jak wyżej:

class HelloTest(object): 

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

    def bar(self): 
     print self.msg 

badanej jednostki jest:

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

Jako kolejna wspomniana odpowiedź, można użyć biblioteki próbnej Python, aby przedstawić twierdzenia na temat wywołania funkcji/metod

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) 

ten patch się metodę bar na HelloTest i zastępuje go atrapa obiektu, który rejestruje rozmowy przeciwko niemu.

Szydełkowanie to trochę dziura od królika. Rób to tylko wtedy, gdy musisz koniecznie, ponieważ sprawia to, że twoje testy są kruchy. Nigdy nie zauważysz zmiany interfejsu API dla wyśmiewanego obiektu na przykład.

2

Dzięki wprowadzeniu @Jordan „s, zakodować to i myślę, że jest to test wykonalne jednostką HelloTest.foo

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

ja nie bardzo rozumiem dlaczego każdy chce sprawdzić, czy wywołuje foo bar.

Foo ma pewną funkcjonalność i ta funkcja musi zostać przetestowana. Jeśli foo używa tego paska, nie powinno to stanowić problemu.

Pożądany wynik to, że po wywołaniu foo(msg), to msg.upper() jest wysyłane na standardowe wyjście.

Możesz redirect stdout to a string buffer i sprawdzić, czy zawartość tego bufora ciągów pasuje do tego, czego oczekujesz.

Przykład:

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

Można również zrobić dla standardowego wejścia (i pisać do standardowego wejścia drwić jakieś wejście) i można podklasy TestIOWrapper jeśli potrzebujesz niczego specjalnego do zrobienia, jak pozwalając tekst non-unicode należy wysłać pod numer sys.stdout bez użycia sys.stdout.buffer (Python 2 vs. Python 3). Istnieje przykład tego w this SO answer. Gdy (wciąż) używasz tylko Pythona 2, użycie StringIO może być lepsze niż użycie modułu io.

Powiązane problemy