В преддверии старта курса "Python QA Engineer" для будущих студентов и всех интересующихся темой тестирования подготовили перевод полезного материала.
Также приглашаем посмотреть подарочное демо-занятие на тему "Карьера QA".
В сообществе тестировщиков много спорят о том, сколько assert-ов должно быть в одном автоматизированном тесте пользовательского интерфейса. Некоторые считают, что на один тест должен приходиться один assert, то есть каждый тест должен проверять только один элемент. Другие же вполне довольны тем, что их тест проверяет сразу несколько элементов.
Какой бы подход вы ни выбрали, я думаю, можно с уверенностью сказать, что тесты должны оставаться ясными, краткими, читаемыми и, конечно же, их должно быть просто поддерживать. Лично у меня не возникает проблем с несколькими assert-ами в одном тесте, поскольку я фокусируюсь на одной функциональной области.
Например, возьмем форму регистрации:
В рамках тестирования формы регистрации пользователей, я, возможно, захочу протестировать сразу несколько вещей. Может я захочу проверить, что сообщение — поздравление с регистрацией отображается корректно, а может проверю, что пользователь будет перенаправлен на страницу входа после прохождения регистрации.
В таком случае я не буду проверять, что пользователь может успешно войти в систему, и оставлю это для другого теста.
Проблема
Проблема встроенного метода assert в Python заключается в том, что он останавливает выполнение теста при первой ошибке. В некоторых сценариях в этом и вправду есть смысл. То есть нет смысла продолжать тест, если часть приложения не работает. Однако бывает и такое, что есть смысл провести все проверки независимо от того, выпадет ли ошибка на одной из них или нет, особенно есть все ваши assert-ы относятся к одной странице в конце теста.
Такой подход мы используем при тестировании веб-сервисов. При проверке тела ответа мы, скорее всего, захотим проверить каждое возвращаемое значение, а затем вывести, какие из них были не такими, как ожидалось, вместо того чтобы останавливать тест при ошибке в первом assert.
Решение: плагин Pytest-check
Pytest-check (от Брайана Оккена) – плагин Pytest, который позволяет вам обернуть все assert-ы теста в один результат pass/fail. Например, если у вас есть 3 assert-а и первый из них выдал fail, то Pytest-check продолжит выполнять оставшиеся 2. Затем он сообщит о том, что тест провалился, если одна или несколько проверок придут с результатом fail.
Python OpenSDK TestProject также поддерживает Pytest, поэтому если у вас уже есть тесты pytest на Selenium, их очень легко преобразовать в тесты TestProject. Вы можете узнать больше об этом в моей статье в блоге HowQA, а также ознакомиться с пошаговым руководством по началу работы здесь.
Давайте рассмотрим, как работает плагин Pytest-check.
Тесты Selenium
Для начала нам понадобится тест. Я воспользуюсь страницей регистрации этого приложения: https://docket-test.herokuapp.com/register
import selenium.webdriver as webdriver
from selenium.webdriver.common.by import By
def test_register_user():
# Arrange
url = "https://docket-test.herokuapp.com/register"
# set the driver instance
driver = webdriver.Chrome()
# browse to the endpoint
driver.get(url)
# maximise the window
driver.maximize_window()
# Act
# Complete registration form
# enter username value
driver.find_element(By.ID, "username").send_keys("Ryan")
# enter email value
driver.find_element(By.ID, "email").send_keys("Test@email.com")
# enter password value
driver.find_element(By.ID, "password").send_keys("12345")
# enter repeat password value
driver.find_element(By.ID, "password2").send_keys("12345")
# click register button
driver.find_element(By.ID, "submit").click()
У нас есть тест, но нужно написать несколько проверок. Давайте сделаем это с помощью метода assert:
# Assert
# confirm registration has been successful
# check if congratulations message contains the correct text
message = driver.find_element(By.XPATH, "/html[1]/body[1]/div[1]/div[1]/div[1]/div[1]/form[1]/div[1]").text
assert message == "Congratulations, you are now registered"
# check user is routed to login page
current_url = driver.current_url
assert current_url == "https://docket-test.herokuapp.com/login"
Если мы их запустим, то все пройдет успешно:
Пока все хорошо, но что случится, если оба assert-а вернутся с результатом fail? Давайте изменим код, чтобы посмотреть, что будет:
# Assert
# confirm registration has been successful
# check if congratulations message contains the correct text
message = driver.find_element(By.XPATH, "/html[1]/body[1]/div[1]/div[1]/div[1]/div[1]/form[1]/div[1]").text
assert message == "Well done, You've Registered"
# check user is routed to login page
current_url = driver.current_url
assert current_url == "https://docket-test.herokuapp.com/register"
driver.quit()
Итак, мы поменяли сообщение, которое ожидаем увидеть и изменили ожидаемый URL, поэтому, когда мы снова выполним этот тест, он вернется с результатом fail:
Как и ожидалось, тест завершился с ошибкой в assert. Отлично, наш тест выполнил свое предназначение, мы нашли ошибку, ну или, что-то вроде того…
Однако из сообщения об ошибке мы видим, что тест упал из-за формулировки сообщения, а URL он даже не проверил. Тест перестает выполняться, как только появляется первый fail, поэтому у нас нет возможности увидеть всю картину. В таком случае нам нужно будет исправить текст ожидаемого сообщения и повторить тест еще раз. Давайте так и сделаем.
Теперь мы поменяли ожидаемое сообщение обратно на «Congratulations, you are now registered», и снова можем запустить тест:
Ага! Тест снова упал, на этот раз из-за неправильного URL.
Знаю, что вы думаете о том, как было бы круто, если бы мы поймали обе этих ошибки за один прогон теста. Что ж, вам повезло, в игру вступает Pytest-check.
Pytest-Check
Установка
Мы можем установить pytest-check через pip install pytest-check
. После того как мы установили pytest-check, его можно импортировать в наш тест.
import pytest_check as check
Теперь, когда все готово, можно немного подправить наши assert-ы. Отныне мы не будем использовать оператор assert, вместо этого мы будем использовать синтаксис pytest-check следующим образом.
Чтобы проверить сообщение воспользуемся функцией check.equal
и добавим туда ожидаемый и фактический текст, например:
check.equal(message, "Congratulations, you are now registered1")
Мы можем сделать то же самое и с проверкой URL-адреса, но сделаем это с помощью другого метода, а именно check.is_in
.
check.is_in("login", current_url)
Полный тест выглядит следующим образом:
import selenium.webdriver as webdriver
from selenium.webdriver.common.by import By
import pytest_check as check
def test_register_user():
# Arrange
url = "https://docket-test.herokuapp.com/register"
# set the driver instance
driver = webdriver.Chrome()
# browse to the endpoint
driver.get(url)
# maximise the window
driver.maximize_window()
# Act
# Complete registration form
# enter username value
driver.find_element(By.ID, "username").send_keys("Ryan8")
# enter email value
driver.find_element(By.ID, "email").send_keys("Test@email8.com")
# enter password value
driver.find_element(By.ID, "password").send_keys("12345")
# enter repeat password value
driver.find_element(By.ID, "password2").send_keys("12345")
# click register button
driver.find_element(By.ID, "submit").click()
# Assert
# confirm registration has been successful
# check if congratulations message contains the correct text
message = driver.find_element(By.XPATH, "/html[1]/body[1]/div[1]/div[1]/div[1]/div[1]/form[1]/div[1]").text
check.equal(message, "Congratulations, you are now registered")
# check user is routed to login page
current_url = driver.current_url
check.is_in("login", current_url)
driver.quit()
На этом этапе мы можем вернуть все ожидаемые значения в исходное состояние, чтобы тест завершился успешно. Так что давайте так и поступим.
Отлично! Теперь давайте посмотрим, что произойдет, если обе проверки вернут fail. Для начала нужно будет поменять код, чтобы они провалились:
# check if congratulations message contains the correct text
message = driver.find_element(By.XPATH, "/html[1]/body[1]/div[1]/div[1]/div[1]/div[1]/form[1]/div[1]").text
check.equal(message, "Congratulations, you are now registered!")
# check user is routed to login page
current_url = driver.current_url
check.is_in("1", current_url)
Запускаем.
Как и в прошлый раз, тест завершился неудачей, как мы и ожидали, но теперь мы видим, что fail вернулся в двух проверках: сначала вывод о том, что содержание сообщения не соответствует ожидаемому, а потом результат проверки URL. Если подойти к вопросу с точки зрения pytest, то можно рассматривать это как один провалившийся тест, но теперь-то мы знаем, что на самом деле несколько проверок вернули fail.
Только если все проверки в тесте будут пройдены успешно, он будет помечен как pass.
Вот такой крутой Pytest-check. Узнать о нем больше вы можете в документации.
yks
Как по мне, так это наоборот какой-то костыль.
Чем проще тест (и не только), чем меньше всяких обёрток, фантиков, тем быстрее, проще, дешевле, приятнее разработка.