Odnoszę wrażenie, że pytest jest popularniejszy. Widziałem już go w użyciu przez różne osoby, czy firmy, natomiast unittest pozostaje w cieniu, ale to moje subiektywne odczucie.
Kilka słów za pytestem prosto z tego tematu:
- easy support for distributed testing
- good plugin architecture
- easier assertions (just assert x == 42, no assertEqual())
- funcargs (since 2.3 or 2.4 called fixtures, somewhat different to what other testing frameworks call fixtures)
Do pytesta dorzuciłbym jeszcze pakiet mock. Od Pythona 3 wchodzi w skład standardowej dystrybucji. Początkowo jego użycie może okazać się dość dziwne - wygląda prosto, ale jeśli nie wczytamy się w dokumentację to możemy się zdziwić, dlaczego to nie działa?
Testy domyślnie poszukiwane są w plikach o nazwach rozpoczynających się od test_, klasach od Test, funkcjach, metodach od test_. Dzieje się to automatycznie, nie musimy nic konfigurować.
Przykładowy test z użyciem pytest oraz mock:
class TestNetwork(object):
def mock_socket(self):
s = MagicMock()
s.connect = MagicMock()
s.send = MagicMock()
s.recv = MagicMock()
s.close = MagicMock()
return s
def setup_method(self, method):
self.socket_mock = self.mock_socket()
@patch('socket.socket')
def test_connect_ipv4_correct_call(self, socket_function_mock):
socket_function_mock.return_value = self.socket_mock
sock = Network.connect_ipv4("127.0.0.1", 80)
socket_function_mock.assert_called_once_with(socket.AF_INET, socket.SOCK_STREAM)
assert socket_function_mock.return_value == sock
sock.connect.assert_called_once_with(("127.0.0.1", 80))
@patch('socket.socket')
def test_connect_ipv4_when_ip_is_wrong(self, socket_function_mock):
socket_function_mock.return_value = self.socket_mock
with pytest.raises(ValueError):
sock = Network.connect_ipv4("127.0.01", 80)
assert socket_function_mock.called == False
W funkcji mock_socket tworzony jest mock, który będzie reprezentował obiekt gniazda z czterema funkcjami - connect, send, recv, close. Dzięki setup_method będzie tworzony przed każdym uruchomieniem metody testowej - dla każdego testu otrzymamy nowy, czysty obiekt. pytest pozwala oczywiście na uruchamianie metod przed startem, czy po zakończeniu metody testu, klasy, modułu. Więcej o tych możliwościach w dokumentacji.
Wkażdym teście wykorzystywany jest również dekorator patch, który pozwala na zamokowanie wybranej funkcji (obiektu, czy metody) w podanym pakiecie. W przykładzie mockowana jest funkcja socket z pakietu socket. I tutaj drobna uwaga, w zależności od tego, w jaki sposób importujemy funkcję socket w testowanej klasie, musimy przesłonić różne nazwy - może być to socket.socket - w przypadku użycia import socket, albo Network.socket - w przypadku użycia from socket import socket. Bardzo dobry opis znajduje się w tym wpisie.
Jeżeli oczekujemy, że testowana metoda rzuci wyjątek używamy konstrukcji with pytest.raises(). Pozostałe asercje są chyba dość zrozumiałe, część z nich pochodzi z pakietu mock.