#python #django #unit-testing
#python #django #модульное тестирование
Вопрос:
Модуль unittest чрезвычайно хорош для обнаружения проблем в коде. Я понимаю идею изоляции и тестирования частей кода с помощью утверждений:
self.assertEqual(web_page_view.func, web_page_url)
Но помимо этих утверждений у вас также может быть некоторая логика перед этим, в том же методе тестирования, которая может вызвать проблемы.
Мне интересно, следует ли учитывать ручную обработку исключений когда-либо внутри методов подкласса TestCase.
Потому что, если я оборачиваю блок в try-catch, если что-то не удается, тест возвращает OK и не завершается сбоем:
def test_simulate_requests(self):
"""
Simulate requests to a url
"""
try:
response = self.client.get('/adress/of/page/')
self.assertEqual(response.status_code, 200)
except Exception as e:
print("error: ", e)
Следует ли всегда избегать обработки исключений в таких тестах?
Комментарии:
1. Обычная практика заключается в использовании
assertRaises
утверждения, когда вы ожидаете, что будет вызвано какое-либо исключение. В других случаях вы должны рассматривать их как обычный тест и не перехватывать какие-либо исключения самостоятельно вручную.
Ответ №1:
Первая часть ответа:
Как вы правильно говорите, перед фактическим тестированием должна быть некоторая логика. Код, принадлежащий модульному тестированию, может быть разделен на четыре части (я использую терминологию Месароша в следующем): настройка, упражнение, проверка, демонтаж. Часто код тестового примера структурирован таким образом, что код для четырех частей четко разделен и находится в этом точном порядке — это называется четырехфазным шаблоном тестирования.
Фаза упражнения — это сердце теста, где выполняется функциональность, которая должна быть проверена в тесте. Настройка гарантирует, что это происходит в четко определенном контексте. Итак, то, что вы описали в этой терминологии, представляет собой ситуацию, когда во время установки что-то выходит из строя. Это означает, что не выполняются предварительные условия, которые требуются для осмысленного выполнения функциональности, подлежащей тестированию.
Это обычная ситуация, и это означает, что вам на самом деле нужно уметь различать три результата теста: тест может пройти успешно, он может потерпеть неудачу или он может просто быть бессмысленным.
К счастью, на это есть ответ в python: Вы можете пропустить тесты, и если тест пропущен, это записывается, но ни как сбой, ни как успех. Пропуск тестов, вероятно, был бы лучшим способом справиться с ситуацией, которую вы показали в своем примере. Вот небольшой фрагмент кода, который демонстрирует один из способов пропуска тестов:
import unittest
class TestException(unittest.TestCase):
def test_skipTest_shallSkip(self):
self.skipTest("Skipped because skipping shall be demonstrated.")
Вторая часть ответа:
Похоже, что в вашем тесте есть некоторые недетерминированные элементы. self.client.get
Может генерировать исключения (но только иногда — иногда это не так). Это означает, что у вас нет контекста во время выполнения теста под контролем. В модульном тестировании это ситуация, которой вы должны стараться избегать. Ваши тесты должны иметь детерминированное поведение.
Один из типичных способов добиться этого — изолировать ваш код от компонентов, которые отвечают за недетерминированность, и во время тестирования заменять эти компоненты mocks. Поведение макетов полностью контролируется тестовым кодом. Таким образом, если ваш код использует какой-либо компонент для доступа к сети, вы бы издевались над этим компонентом. Затем в некоторых тестовых примерах вы можете указать макету имитировать успешное сетевое взаимодействие, чтобы увидеть, как ваш компонент справляется с этим, а в других тестах вы указываете макету имитировать сбой сети, чтобы увидеть, как ваш компонент справляется с этой ситуацией.
Ответ №2:
Есть два «плохих» состояния теста: сбой (когда одно из утверждений завершается неудачей) и Ошибка (когда сам тест завершается неудачей — ваш случай).
Прежде всего, само собой разумеется, что лучше построить свой тест таким образом, чтобы он достигал своих утверждений.
Если вам нужно подтвердить, что какой-либо протестированный код вызывает исключение, вы должны использовать with self.assertRaises(ExpectedError)
Если какой-то код внутри теста вызывает исключение — лучше узнать это по результату «Ошибка», чем увидеть «ОК, все тесты пройдены»
Если ваша тестовая логика действительно предполагает, что что-то может не сработать в самом тесте, и это нормальное поведение — вероятно, тест неверен. Возможно, вам следует использовать mocks (https://docs.python.org/3/library/unittest.mock.html ) для имитации вызова api или чего-то еще.
В вашем случае, даже если тест завершается неудачей, вы перехватываете его с помощью простого except и говорите «Ок, продолжайте». В любом случае реализация неправильная.
Наконец: нет, не должно быть, кроме как в ваших тестовых примерах
P.S. лучше вызывать ваши тестовые функции с помощью test_what_you_want_to_test_name , в этом случае, вероятно, test_successful_request будет в порядке.