Вы правы, ваши тесты не должны проверять, что random
модуль выполняет свою работу; unittest должен только проверять сам класс, а не то, как он взаимодействует с другим кодом (который должен быть проверен отдельно).
Конечно, вполне возможно, что ваш код использует random.randint()
неправильно; или вы random.randrange(1, self._sides)
вместо этого звоните, и ваш кубик никогда не выбрасывает наивысшее значение, но это будет другой тип ошибки, а не тот, который вы могли бы обнаружить с помощью юнит-теста. В этом случае ваше die
устройство работает так, как задумано, но сама конструкция была ошибочной.
В этом случае, я хотел бы использовать насмешливый , чтобы заменить на randint()
функцию, и только проверить , что это было называется правильно. Python 3.3 и выше поставляется с unittest.mock
модулем для выполнения этого типа тестирования, но вы можете установить внешний mock
пакет на более старые версии, чтобы получить точно такую же функциональность
import unittest
try:
from unittest.mock import patch
except ImportError:
# < python 3.3
from mock import patch
@patch('random.randint', return_value=3)
class TestDice(unittest.TestCase):
def _make_one(self, *args, **kw):
from die import Die
return Die(*args, **kw)
def test_standard_size(self, mocked_randint):
die = self._make_one()
result = die.roll()
mocked_randint.assert_called_with(1, 6)
self.assertEqual(result, 3)
def test_custom_size(self, mocked_randint):
die = self._make_one(sides=42)
result = die.roll()
mocked_randint.assert_called_with(1, 42)
self.assertEqual(result, 3)
if __name__ == '__main__':
unittest.main()
С насмешками ваш тест теперь очень прост; на самом деле есть только 2 случая. Случай по умолчанию для 6-стороннего кристалла и пользовательский случай.
Существуют и другие способы временно заменить randint()
функцию в глобальном пространстве имен Die
, но mock
модуль делает это проще всего. @mock.patch
Декоратор здесь применяется ко всем методам испытаний в тесте; каждому тестовому методу передается дополнительный аргумент, random.randint()
проверяемая функция, поэтому мы можем проверить макет, чтобы убедиться, что он действительно был вызван правильно. В return_value
аргумент указывает , что возвращаемые из издеваться , когда его называют, так что мы можем проверить , что die.roll()
метод действительно вернул «случайный» результат для нас.
Я использовал другую лучшую практику тестирования Python: импортируйте тестируемый класс как часть теста. _make_one
Метод делает импорт и инстанцирования работу в рамках теста , так что тест модуль все равно будет загружать даже если вы сделали ошибку синтаксиса или другую ошибку , которая будет препятствовать оригинальный модуль для импорта.
Таким образом, если вы допустили ошибку в самом коде модуля, тесты все равно будут запущены; они просто потерпят неудачу, сообщая вам об ошибке в вашем коде.
Чтобы было ясно, вышеприведенные тесты чрезвычайно упрощены. random.randint()
Например, цель не в том, чтобы проверить, что было вызвано с правильными аргументами. Вместо этого цель состоит в том, чтобы проверить, что устройство дает правильные результаты при определенных входных данных, где эти входные данные включают результаты других не тестируемых устройств. Посмеиваясь над этим random.randint()
методом, вы получаете контроль над еще одним вводом кода.
В реальных тестах реальный код в тестируемом модуле будет более сложным; связь с входными данными, передаваемыми в API, и то, как затем вызываются другие модули, могут быть по-прежнему интересными, и насмешка даст вам доступ к промежуточным результатам, а также позволит установить значения возврата для этих вызовов.
Например, в коде, который проверяет подлинность пользователей на основе сторонней службы OAuth2 (многоэтапное взаимодействие), вы хотите проверить, передает ли ваш код нужные данные этой сторонней службе и позволяет ли вы выводить различные сообщения об ошибках, которые Сторонний сервис вернется, что позволит вам моделировать различные сценарии, не создавая полноценный сервер OAuth2 самостоятельно. Здесь важно проверить, что информация из первого ответа была правильно обработана и передана вызову второго этапа, поэтому вы хотите убедиться, что проверяемая служба вызывается правильно.