#python #python-unittest
Вопрос:
Я пишу модульные тесты с повторяющимся кодом, которые я хотел бы упростить. В моем тестовом примере выполняется несколько сценариев, в которых ожидается сбой HTTP-вызова с указанным кодом состояния и сообщением об ошибке. Я использую unittest.assertRaises()
, вот так:
with self.assertRaises(MyHTTPClientError) as er:
await my_http_request()
self.assertEqual(er.exception.code, 404, er.exception)
error_msg = json.loads(er.exception.response.body)["error_description"]
self.assertEqual(error_msg, "Not found")
Чтобы сделать код более СУХИМ и удобным для чтения, я хотел бы написать пользовательский метод assertRaisesHTTPError()
в своем классе тестового случая, чтобы приведенный выше фрагмент был упрощен до:
with self.assertRaisesHTTPError(404, "Not found") as er:
await my_http_request()
Я застрял в написании кода. До сих пор у меня это получалось, но я не знаю, как вызвать «тело» оператора with.
class MyTestCase(unittest.TestCase):
def assertRaisesHTTPError(self, code: int, error_message: str, *args: Any,
**kwargs: Any) -> Any:
with self.assertRaises(MyHTTPClientError) as er:
### What goes here ? ###
self.assertEqual(er.exception.code, code)
actual_error_message = json.loads(
er.exception.response.body)["error_description"]
self.assertEqual(actual_error_message, error_message)
return er
Комментарии:
1. Это было бы то же самое, что и тело вне метода (т. Е.
await my_http_request()
). Есть ли причина, по которой вы можете просто назвать это?2.
my_http_request()
это просто держатель места. Обычно для этого требуются аргументы, которые варьируются в зависимости от сценария.3. можете ли вы просто перейти
*args
и**kwargs
к методу? что-то вродеawait my_http_request(*args, **kwargs)
? Если нет, вы можете просто добавить еще два аргументаassertRaisesHTTPError
, чтобы принять аргументы метода за вас и передать их таким же образом4. Иногда необходимы дополнительные вызовы методов. Поэтому в идеале я хотел бы вызвать все тело оператора with, не делая никаких предположений о его содержании.
Ответ №1:
Поскольку вам нужно уметь обрабатывать несколько разных тел, лучше всего, вероятно, использовать ссылку на функцию в качестве аргумента для вашего пользовательского утверждения. Например:
from typing import Callable
class MyTestCase(unittest.TestCase):
def assertRaisesCustom(self, func: Callable[[], None]) -> Any:
with self.assertRaises(MyHTTPClientError) as er:
func()
def test_something(self):
def do_something(n: int):
raise ValueError("something bad happened")
self.assertRaisesCustom(lambda: do_something(n))