Piszę testy dla funkcji takiej jak następna:
def foo():
print 'hello world!'
Więc kiedy chcę przetestować tę funkcję, kod będzie wyglądał tak:
import sys
from foomodule import foo
def test_foo():
foo()
output = sys.stdout.getline().strip() # because stdout is an StringIO instance
assert output == 'hello world!'
Ale jeśli uruchomię testy nosetesty z parametrem -s, test zawiesza się. Jak mogę złapać wyjście za pomocą modułu unittest lub nosowego?
python
unit-testing
nosetests
python-nose
Pedro Valencia
źródło
źródło
with mock.patch('sys.stdout', new_callable=StringIO.StringIO):
pypi.python.org/pypi/mockOdpowiedzi:
Używam tego menedżera kontekstu do przechwytywania danych wyjściowych. Ostatecznie wykorzystuje tę samą technikę, co niektóre inne odpowiedzi, tymczasowo zastępując
sys.stdout
. Wolę menedżera kontekstu, ponieważ zawija on całą księgowość w jedną funkcję, więc nie muszę ponownie pisać żadnego kodu próbnego i nie muszę pisać funkcji konfiguracji i dezaktywacji tylko w tym celu.Użyj tego w ten sposób:
Ponadto, ponieważ pierwotny stan wyjściowy jest przywracany po wyjściu z
with
bloku, możemy ustawić drugi blok przechwytywania w tej samej funkcji co pierwszy, co nie jest możliwe przy użyciu funkcji konfiguracji i porzucania, i staje się rozwlekłe podczas pisania try-last blokuje się ręcznie. Zdolność ta przydała się, gdy celem testu było porównanie wyników dwóch funkcji względem siebie, a nie z jakąś wstępnie obliczoną wartością.źródło
TypeError: unicode argument expected, got 'str'
(typ przekazany do print (str / unicode) jest nieistotny).from io import BytesIO as StringIO
aw Pythonie 3 po prostufrom io import StringIO
. Wydaje mi się, że rozwiązało to problem w moich testach.strip()
dounicode
powrotuStringIO.getvalue()
?sys
. Za pomocą instrukcji importu tworzysz zmienną lokalną o nazwie,stderr
która otrzymała kopię wartości w formaciesys.stderr
. Zmiany jednego nie są odzwierciedlane w drugim.Jeśli naprawdę chcesz to zrobić, możesz ponownie przypisać sys.stdout na czas trwania testu.
Gdybym jednak pisał ten kod, wolałbym przekazać
out
dofoo
funkcji opcjonalny parametr .Wtedy test jest znacznie prostszy:
źródło
StringIO
klasa musi teraz zostać zaimportowana zio
modułu.from io import StringIO
działa w Pythonie 2.6+.from io import StringIO
w Pythonie 2,TypeError: unicode argument expected, got 'str'
podczas drukowania otrzymasz .with redirect_stdout(out):
saved_stdout = sys.stdout
, zawsze masz do tego magiczne odniesieniesys.__stdout__
, np. Potrzebujesz tylkosys.stdout = sys.__stdout__
do sprzątania.Od wersji 2.7 nie ma już potrzeby ponownego przypisywania
sys.stdout
, zapewnia tobuffer
flaga . Ponadto jest to domyślne zachowanie nosetest.Oto przykład niepowodzenia w kontekście niebuforowanym:
Można ustawić bufor poprzez
unit2
flagi linii poleceń-b
,--buffer
lub wunittest.main
opcji. Odwrotność osiąga się za pomocąnosetest
flagi--nocapture
.źródło
--nocapture
; w szczególności, jeśli ta flaga jest ustawiona, tryb buforowany zostanie wyłączony. Masz więc możliwość zobaczenia wyjścia na terminalu lub sprawdzenia, czy wyjście jest zgodne z oczekiwaniami.Wiele z tych odpowiedzi zawiodło, ponieważ nie możesz tego zrobić
from StringIO import StringIO
w Pythonie 3. Oto minimalny działający fragment oparty na komentarzu @ naxa i książce kucharskiej Pythona.źródło
W Pythonie 3.5 możesz używać
contextlib.redirect_stdout()
iStringIO()
. Oto modyfikacja Twojego koduźródło
redirect_stdout()
iredirect_stderr()
zwraca ich argument wejściowy. Więcwith contextlib.redirect_stdout(StringIO()) as temp_stdout:
daje wszystko w jednej linii. Przetestowano pod 3.7.1.Dopiero uczę się języka Python i zmagam się z podobnym problemem, jak ten powyżej, z testami jednostkowymi dla metod z danymi wyjściowymi. Mój pozytywny test jednostkowy dla modułu foo powyżej zakończył się następująco:
źródło
sys.stdout.getvalue().strip()
i nie oszukiwać w porównaniu z\n
:)from io import StringIO
Pisanie testów często pokazuje nam lepszy sposób pisania naszego kodu. Podobnie jak w przypadku odpowiedzi Shane'a, chciałbym zasugerować jeszcze inny sposób spojrzenia na to. Czy naprawdę chcesz zapewnić, że twój program wypisał określony ciąg, czy po prostu skonstruował określony ciąg na wyjście? Staje się to łatwiejsze do przetestowania, ponieważ prawdopodobnie możemy założyć, że instrukcja Pythona
print
działa poprawnie.Wtedy twój test jest bardzo prosty:
Oczywiście, jeśli naprawdę potrzebujesz przetestować rzeczywiste wyniki programu, możesz to zignorować. :)
źródło
foo()
nie robi nic poza wywołaniem instrukcji print, prawdopodobnie nie stanowi to problemu.Na podstawie odpowiedzi Roba Kennedy'ego napisałem wersję menedżera kontekstu opartą na klasach, aby buforować dane wyjściowe.
Sposób użycia jest następujący:
Oto implementacja:
źródło
Lub rozważ użycie
pytest
, ma wbudowaną obsługę potwierdzania stdout i stderr. Zobacz dokumentacjęźródło
Zarówno n611x007, jak i Noumenon już sugerowały użycie
unittest.mock
, ale ta odpowiedź dostosowuje Acumenus, aby pokazać, jak łatwo można opakowaćunittest.TestCase
metody do interakcji z wyśmiewanymstdout
.źródło
Opierając się na wszystkich niesamowitych odpowiedziach w tym wątku, oto jak to rozwiązałem. Chciałem, aby był jak najbardziej zapasowy. Rozszerzyłem mechanizm testów jednostkowych za pomocą
setUp()
przechwytywaniasys.stdout
isys.stderr
dodałem nowe interfejsy API potwierdzania, aby sprawdzić przechwycone wartości względem oczekiwanej wartości, a następnie przywrócićsys.stdout
isys.stderr
potearDown(). I did this to keep a similar unit test API as the built-in
unittestAPI while still being able to unit test values printed to
sys.stdoutor
sys.stderr`.Po uruchomieniu testu jednostkowego dane wyjściowe to:
źródło