[Тестирование IT-систем, Python, Тестирование мобильных приложений] Передача динамических объектов от setup к тестовой функции в py.test
Автор
Сообщение
news_bot ®
Стаж: 6 лет 9 месяцев
Сообщений: 27286
В больших проектах в какой-то момент получается ситуация, когда тестов на проекте уже много и параллельно развивается собственный высокоуровневый фреймворк. Фреймворк, в данном случае, как обертка над функциями объекта тестирования и возможностями различных инструментов которые используются на проекте. Кроме того все папки заполнены фикстурами, многие из которых используются только в одном тестовом файле.
В этот прекрасный момент возникают некоторые проблемы. Про одну из них я уже писал, это реализация удобной параметризации, например из файла. Про следующую, из наиболее злосчастных, поговорим в этой статье.
«Капитан, у нас много фикстур и появляются глобалы»
Перегрузка тестовых директорий фикстурами это вполне логичное следствие использования концепта который заложен в py.test, но иногда такой подход переходит за рамки приемлемого. Кроме того, мы часто можем наблюдать в тестах конструкции, которые призваны определить какую информацию надо взять тесту в отдельном определенном случае или желание изначально проверить возможность дальнейшего прохождения теста еще на стадии подготовки среды.
Самое большое заблуждение, в ситуации когда эти конструкции передаются из некой setup фикстуры и идут вдоль всего теста, — использовать global переменные. Я столкнулся с подобными тяжелыми случаями и эта идея приходит на ум одной из первых.
В этом месте стоит упомянуть, что концепция фикстур позволяет избежать такой порчи чистоты кода, но также даёт ещё дополнительные уровни абстракции и множество референсов. На крайний случай можно завести ужасную привычку распаковывать результат фикстуры, но в таком случае мы портим логи, т.к. у нас нет разделения на Setup, Run и Teardown, и дополнительно усложняем код в момент распаковки результатов или плодим множественные шорткаты.
Рассмотрим на примерах, и начнем с ужасного:
"Фикстуры и global"
import pytest
@pytest.fixture(autouse=True)
def setup(create_human, goto_room, goto_default_position, choose_window, get_current_view):
global human
global window
# Сделаем подготовительные действия
desired_room = 1 # Можем этот момент параметризировать, в зависимости от задачи
human = create_human("John", "Doe") # Допускаем что один и тот же человек во всех случаях
# Если что-то пошло не по плану, то дальше нет смысла продолжать
assert goto_room(human, desired_room), "{} не дошел в комнату {}".format(human.full_name, desired_room)
# Выбираем случайное окно
window = choose_window(desired_room)
view = get_current_view(window)
assert view, "В окне {} ничего нет".format (window)
yield
# В Teardown вернёмся в начальное место
goto_default_position(human)
@pytest.mark.parametrize(
"city, expected_result",
[
("New York", False),
("Berlin", False),
("Unknown", True)
]
)
def test_city_in_window(city, expected_result):
"""Проверим что за вид у нас за окном."""
window_view = human.look_into(window)
recognized_city = human.recognize_city(window_view)
assert (recognized_city == city) == expected_result, "Мы увидели город которого нет"
В результате:
- Есть первоначальные проверки
- Есть злосчастный global
"Распаковка во всей красе"
import pytest
@pytest.fixture
def setup(create_human, goto_room, goto_default_position, choose_window, get_current_view):
# Сделаем подготовительные действия
desired_room = 1 # Можем этот момент параметризировать, в зависимости от задачи
human = create_human("John", "Doe") # Допускаем что один и тот же человек во всех случаях
# Если что-то пошло не по плану, то дальше нет смысла продолжать
assert goto_room(human, desired_room), "{} не дошел в комнату {}".format(human.full_name, desired_room)
# Выбираем случайное окно
window = choose_window(desired_room)
view = get_current_view(window)
assert view, "В окне {} ничего нет".format (window)
yield { "human": human, "window": window}
# В Teardown вернёмся в начальное место
goto_default_position(human)
@pytest.mark.parametrize(
"city, expected_result",
[
("New York", False),
("Berlin", False),
("Unknown", True)
]
)
def test_city_in_window(setup, city, expected_result):
"""Проверим что за вид у нас за окном."""
data = setup
window_view = data["human"].look_into(data["window"])
recognized_city = data["human"].recognize_city(window_view)
assert (recognized_city == city) == expected_result, "Мы увидели город которого нет"
В результате:
- Непонятный шорткат на результат фикстуры setup
- Распаковка малоинформативная или может занять пару, а то и десяток, строк
Если на маленьком примере это не выглядит так критично, то когда тест разрастается до 400+ строк, это начинает бросаться в глаза.
Маленькая хитрость про классы
Так как же поступить, если мы видим перед собой 8 фикстур в setup из которого нам надо достать: и несколько фикстур после использования для предустановок теста, и данные которые мы обработаем и провалидируем вне рамок нашего кейса?
Это звоночек — настало время взять инстанс класса и передать всё через него. Сам инстанс за нас создаст py.test, поэтому нам надо будет просто указать его атрибуты.
Усовершенствуем наш пример:
import pytest
class TestWindowView:
@pytest.fixture
def setup(self, create_human, goto_room, goto_default_position, choose_window, get_current_view):
# Сделаем подготовительные действия
desired_room = 1 # Можем этот момент параметризировать, в зависимости от задачи
self.human = create_human("John", "Doe") # Допускаем что один и тот же человек во всех случаях
# Если что-то пошло не по плану, то дальше нет смысла продолжать
assert goto_room(self.human, desired_room), "{} не дошел в комнату {}".format(human.full_name, desired_room)
# Выбираем случайное окно
self.window = choose_window(desired_room)
view = get_current_view(self.window)
assert view, "В окне {} ничего нет".format (self.window)
yield
# В Teardown вернёмся в начальное место
goto_default_position(self.human)
@pytest.mark.parametrize(
"city, expected_result",
[
("New York", False),
("Berlin", False),
("Unknown", True)
]
)
def test_city_in_window(self, setup, city, expected_result):
"""Проверим что за вид у нас за окном."""
window_view = self.human.look_into(self.window)
recognized_city = self.human.recognize_city(window_view)
assert (recognized_city == city) == expected_result, "Мы увидели город которого нет"
В результате:
- Чистая и внятная передача нужных объектов
- Никаких распаковок и global
Про целесообразность метода
С точки зрения целостности взятый пример кажется невнятными и полноценно заменяемым другими подходами, но в данном случае этот пример стоит учитывать только как демонстрацию подобной возможности и возможность оценить лаконичность итогового кода.
По сути же, речь идёт сугубо о динамических объектах для последующего тестирования. Это также касается удобных шорткатов для дальнейшего использования в тех случаях, когда состояние среды не зависит от входных параметров и во многом зависит от внешних факторов.
На мой взгляд наиболее подходящими сюжетами использования описанного подхода является работа с Android/iOS приложениями через Appium и тестирование IOT/Embedded устройств.
===========
Источник:
habr.com
===========
Похожие новости:
- [Python, Node.JS, Серверное администрирование, TypeScript, Игры и игровые приставки] Пишем матчмейкинг для Доты 2014 года
- [Разработка мобильных приложений, Разработка под Android, Тестирование мобильных приложений] Автотесты на Android. Картина целиком
- [Тестирование IT-систем, Big Data, Исследования и прогнозы в IT] Как сократить издержки на автотестах
- [Python, Программирование, Java, ООП] Почему здравый смысл важнее паттернов, а Active Record не так уж и плох
- [Apache, Высокая производительность, Системы обмена сообщениями, Тестирование IT-систем] Поднимаем тестовое окружение в Kubernetes: Apache Kafka, Kafka Connect, MirrorMaker 2.0 и Jmeter
- [Разработка веб-сайтов, Open source, Тестирование IT-систем, Программирование, Клиентская оптимизация] Меряем изменения в скорости загрузки сайта в различных сетевых (и не только) условиях. Теперь удобнее
- [Python, Программирование, Параллельное программирование, Машинное обучение] Как писать аккуратные конвейеры для машинного обучения (перевод)
- [Python, Обработка изображений, Искусственный интеллект, TensorFlow] Распознавание мяча в волейболе с OpenCV и Tensorflow
- [Python, Машинное обучение, Спортивное программирование] Конфуций и Маргарита
- [Python, Программирование, API, ВКонтакте API] VKWave — фреймворк для разработки ботов ВКонтакте
Теги для поиска: #_testirovanie_itsistem (Тестирование IT-систем), #_python, #_testirovanie_mobilnyh_prilozhenij (Тестирование мобильных приложений), #_python, #_pytest, #_appium, #_quality_assurance, #_testing, #_test_automation, #_testirovanie_itsistem (
Тестирование IT-систем
), #_python, #_testirovanie_mobilnyh_prilozhenij (
Тестирование мобильных приложений
)
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 23:53
Часовой пояс: UTC + 5
Автор | Сообщение |
---|---|
news_bot ®
Стаж: 6 лет 9 месяцев |
|
В больших проектах в какой-то момент получается ситуация, когда тестов на проекте уже много и параллельно развивается собственный высокоуровневый фреймворк. Фреймворк, в данном случае, как обертка над функциями объекта тестирования и возможностями различных инструментов которые используются на проекте. Кроме того все папки заполнены фикстурами, многие из которых используются только в одном тестовом файле. В этот прекрасный момент возникают некоторые проблемы. Про одну из них я уже писал, это реализация удобной параметризации, например из файла. Про следующую, из наиболее злосчастных, поговорим в этой статье. «Капитан, у нас много фикстур и появляются глобалы» Перегрузка тестовых директорий фикстурами это вполне логичное следствие использования концепта который заложен в py.test, но иногда такой подход переходит за рамки приемлемого. Кроме того, мы часто можем наблюдать в тестах конструкции, которые призваны определить какую информацию надо взять тесту в отдельном определенном случае или желание изначально проверить возможность дальнейшего прохождения теста еще на стадии подготовки среды. Самое большое заблуждение, в ситуации когда эти конструкции передаются из некой setup фикстуры и идут вдоль всего теста, — использовать global переменные. Я столкнулся с подобными тяжелыми случаями и эта идея приходит на ум одной из первых. В этом месте стоит упомянуть, что концепция фикстур позволяет избежать такой порчи чистоты кода, но также даёт ещё дополнительные уровни абстракции и множество референсов. На крайний случай можно завести ужасную привычку распаковывать результат фикстуры, но в таком случае мы портим логи, т.к. у нас нет разделения на Setup, Run и Teardown, и дополнительно усложняем код в момент распаковки результатов или плодим множественные шорткаты. Рассмотрим на примерах, и начнем с ужасного: "Фикстуры и global" import pytest
@pytest.fixture(autouse=True) def setup(create_human, goto_room, goto_default_position, choose_window, get_current_view): global human global window # Сделаем подготовительные действия desired_room = 1 # Можем этот момент параметризировать, в зависимости от задачи human = create_human("John", "Doe") # Допускаем что один и тот же человек во всех случаях # Если что-то пошло не по плану, то дальше нет смысла продолжать assert goto_room(human, desired_room), "{} не дошел в комнату {}".format(human.full_name, desired_room) # Выбираем случайное окно window = choose_window(desired_room) view = get_current_view(window) assert view, "В окне {} ничего нет".format (window) yield # В Teardown вернёмся в начальное место goto_default_position(human) @pytest.mark.parametrize( "city, expected_result", [ ("New York", False), ("Berlin", False), ("Unknown", True) ] ) def test_city_in_window(city, expected_result): """Проверим что за вид у нас за окном.""" window_view = human.look_into(window) recognized_city = human.recognize_city(window_view) assert (recognized_city == city) == expected_result, "Мы увидели город которого нет" В результате:
"Распаковка во всей красе" import pytest
@pytest.fixture def setup(create_human, goto_room, goto_default_position, choose_window, get_current_view): # Сделаем подготовительные действия desired_room = 1 # Можем этот момент параметризировать, в зависимости от задачи human = create_human("John", "Doe") # Допускаем что один и тот же человек во всех случаях # Если что-то пошло не по плану, то дальше нет смысла продолжать assert goto_room(human, desired_room), "{} не дошел в комнату {}".format(human.full_name, desired_room) # Выбираем случайное окно window = choose_window(desired_room) view = get_current_view(window) assert view, "В окне {} ничего нет".format (window) yield { "human": human, "window": window} # В Teardown вернёмся в начальное место goto_default_position(human) @pytest.mark.parametrize( "city, expected_result", [ ("New York", False), ("Berlin", False), ("Unknown", True) ] ) def test_city_in_window(setup, city, expected_result): """Проверим что за вид у нас за окном.""" data = setup window_view = data["human"].look_into(data["window"]) recognized_city = data["human"].recognize_city(window_view) assert (recognized_city == city) == expected_result, "Мы увидели город которого нет" В результате:
Если на маленьком примере это не выглядит так критично, то когда тест разрастается до 400+ строк, это начинает бросаться в глаза. Маленькая хитрость про классы Так как же поступить, если мы видим перед собой 8 фикстур в setup из которого нам надо достать: и несколько фикстур после использования для предустановок теста, и данные которые мы обработаем и провалидируем вне рамок нашего кейса? Это звоночек — настало время взять инстанс класса и передать всё через него. Сам инстанс за нас создаст py.test, поэтому нам надо будет просто указать его атрибуты. Усовершенствуем наш пример: import pytest
class TestWindowView: @pytest.fixture def setup(self, create_human, goto_room, goto_default_position, choose_window, get_current_view): # Сделаем подготовительные действия desired_room = 1 # Можем этот момент параметризировать, в зависимости от задачи self.human = create_human("John", "Doe") # Допускаем что один и тот же человек во всех случаях # Если что-то пошло не по плану, то дальше нет смысла продолжать assert goto_room(self.human, desired_room), "{} не дошел в комнату {}".format(human.full_name, desired_room) # Выбираем случайное окно self.window = choose_window(desired_room) view = get_current_view(self.window) assert view, "В окне {} ничего нет".format (self.window) yield # В Teardown вернёмся в начальное место goto_default_position(self.human) @pytest.mark.parametrize( "city, expected_result", [ ("New York", False), ("Berlin", False), ("Unknown", True) ] ) def test_city_in_window(self, setup, city, expected_result): """Проверим что за вид у нас за окном.""" window_view = self.human.look_into(self.window) recognized_city = self.human.recognize_city(window_view) assert (recognized_city == city) == expected_result, "Мы увидели город которого нет" В результате:
Про целесообразность метода С точки зрения целостности взятый пример кажется невнятными и полноценно заменяемым другими подходами, но в данном случае этот пример стоит учитывать только как демонстрацию подобной возможности и возможность оценить лаконичность итогового кода. По сути же, речь идёт сугубо о динамических объектах для последующего тестирования. Это также касается удобных шорткатов для дальнейшего использования в тех случаях, когда состояние среды не зависит от входных параметров и во многом зависит от внешних факторов. На мой взгляд наиболее подходящими сюжетами использования описанного подхода является работа с Android/iOS приложениями через Appium и тестирование IOT/Embedded устройств. =========== Источник: habr.com =========== Похожие новости:
Тестирование IT-систем ), #_python, #_testirovanie_mobilnyh_prilozhenij ( Тестирование мобильных приложений ) |
|
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 23:53
Часовой пояс: UTC + 5