Dopasowanie czegokolwiek - mock.ANY
Odpowiednik testing::_ w GoogleMock, czyli akceptowanie każdego argumentu z jakim zostanie wywołany mock, co pozwala na rozluźnienie zależności (coupling).class Gearbox:
def moveGear(self, gearLevel):
print('Gear to level: ' + str(gearLevel))
Testclass TestVehicle(unittest.TestCase):
def test_tractorStartOnRandomGear(self):
mock_gearbox = mock.create_autospec(Gearbox)
tractor = Vehicle(mock_gearbox)
tractor.runEngine()
mock_gearbox.moveGear.assert_called_once_with(mock.ANY)
Mock vs. MagicMock vs. create_autospec
Z tego co udało mi się zrozumieć MagicMock-i, różnią się od zwykłych Mock-ów tym, że posiadają predefiniowane wartości dla pewnych pospolitych metod np.: __len__ == 1.Ponieważ mock.Mock i mock.MagicMock akceptują wołania z dowolną ilością argumentów, najlepiej korzystać mock.create_autospec. Taki mock w przypadku gdy ilość argumentów się nie zgadza rzuci wyjątek.
Dekorator @mock.patch zamiast dependency injection
Dekoratory z biblioteki mock, pozwalają na zgrabne przesłanianie klas i modułów w testowanym kodzie, dzięki czemu korzystamy z dobrodziejstw dependency injection, bez niepotrzebnego rozbudowywania "konstruktorów".Kolejność dekoratorów jest istotna
Każdy moduł importuje inne moduły do swojej lokalnej przestrzeni, dlatego należy pamiętać, by nie mock-ować modułów (np. os) w pliku z testami (bo tylko tutaj będzie zmock-owany). Należy explicit wskazać na moduł gdzie jest nasz testowany kod (np. mycode.os).Kiedy korzysta się z wielu dekoratorów należy pamiętać o ich kolejności. Mapowanie dekoratorów do parametrów następuje w odwrotnej kolejności niż deklaracje. Przykład poniżej.
import os
import sys
class FilesSystem:
def countFolders(self):
l = [dir for dir in os.listdir('.') if os.path.isdir(dir)]
sys.exit(len(l))
Test import unittest
import mock
import asdf
class TestFileSystem(unittest.TestCase):
@mock.patch('asdf.os')
@mock.patch('asdf.sys')
def test_countFolders(self, mock_sys, mock_os):
mock_os.listdir.return_value = ['folder1', 'folder2']
mock_os.path.isdir.return_value = True
fs = FilesSystem()
fs.countFolders()
mock_os.listdir.assert_called_once_with('.')
expected = [call.mock_os.path.attribute.isdir('folder1'),
call.mock_os.path.attribute.isdir('folder2')]
self.assertTrue(mock_os.path.isdir.mock_calls == expected)
mock_sys.exit.assert_called_once_with(2)
Innym rodzajem dekoratora jest @mock.patch.object, który pozwala na z-mockowanie wybranej metody z klasy, gdy cała reszta nadal będzie wołała oryginalną implementację. class Accounts:
def isUser(self, user):
print('isUser ' + user)
def assign(self, user, movie):
print('assign ' + user + ' ' + movie)
Test class TestVideoLib(unittest.TestCase):
@mock.patch.object(Accounts, 'isUser')
def test_whenUserExist_thenHeCouldRentAMovie(self, mock_isUser):
mock_isUser.return_value = True
accounts = Accounts()
videoLib = VideoLib(accounts)
videoLib.rent('Jhon', 'Titanic')
mock_isUser.assert_called_once_with('Jhon')
Otwarte kwestie: Odpowiednik StrictMock, mock-owanie metody open, list comprehension - które niekiedy sprawa mi problemy, gdy chce jest otestować.
Brak komentarzy:
Prześlij komentarz