Automated REST API Testing with Python
Originally posted: https://www.dowen.me.uk/Automated-rest-api-testing-with-python/ Last time out I introduced you to the start of my journey to automate all the «test» things in Python. I continue that journey now by switching focus away from frontend, to Restful Web APIs. You can look back at my last post here.
Explore, Request, Assert
Exploring APIs
Before you can make requests and you need to understand the API your testing. For this I recommend using a Rest Client. While you may already have mad skills in cURL, for the rest of us I suggest using Postman, Insomnia or SoapUI. If, like me, you want to develop your API testing skills in a safe place, outside of any work projects, there are some great options! Ministry of Testing has a resource page listing some of those options Websites To Practice Testing. The one I am using is Restful Booker by Mark Winteringham. Start your exploration using API documentation. For Restful booker, you can find that here. To learn more about testing APIs and using Postman, I suggest the free course Exploring Service APIs through Test Automation by Amber Race.
Making Requests
Inspired by the article API Integration in Python – Part 1, I started by making a Python client to abstract interactions with the Restful Booker API. The article is not focused on testing, but instead shows us how to Constructing an API Library, using Requests. This pattern of abstraction is great and we can use it along side an assertion framework to do some robust testing. You can take a closer look at my API Library for Restful Booker, and my rest code on the Pybooker GitHub Repository. This basic example makes a GET request to the URL https://restful-booker.herokuapp.com/booking/1/, and prints the resulting JSON response body into the Python console. Running it gives us: Because the method returns the response object, we can not only get the JSON body, but also useful information like the HTTP Status code. Requests can make use of a wide range of HTTP methods, explore it and see what you can do!
Asserting That
OK so now we can make Requests, and access the returned response in an object. With this, we can start implementing some automated checks, to see if we are getting back what we expect. To do this, I have made use of the module pyassert, you could also use other assertion libraries such as fluentcheck. I am using Pytest to run my tests. While you can assert on almost every aspect of the response, the most basic check is the response returned ‘OK’. This means we made a valid request, and the service didn’t throw an internal server error. One step further would be to assert on the HTTP Status code. For example for a GET you expect to succeed you might expect «200 Success», and for creating a new item with POST you might expect «201 Created». You can read more about http status codes HTTP STATUS DOGS or HTTP Cats.
Home
В этой короткой серии статей я хочу исследовать библиотеку запросов Python и то, как ее можно использовать для создания тестов REST API. Это первая статья, и она посвящена тому, как начать и написать первые тесты для выборочного REST API.
Для начала нам понадобится свежая установка интерпретатора Python, который можно скачать здесь. Затем нужно создать новый проект в IDE (я использую PyCharm, но сойдет любая приличная IDE) и установить библиотеку запросов. Проще всего использовать pip, менеджер пакетов Python:
Нам также понадобится фреймворк юнит-тестирования для запуска тестов, библиотеки утверждения и базовой функциональности отчетов. Я предпочитаю pytest, но запросы так же хорошо работают с любым другим фреймворком юнит-тестирования Python.
Тестируемое API
Для примеров в этой статье я буду использовать REST API Zippopotam.us. Этот API берет код страны и индекс, и возвращает данные о геолокации, соответствующей этой стране и индексу. К примеру, GET-запрос к http://api.zippopotam.us/us/90210 вернет код HTTP-статуса 200 и вот такое тело ответа JSON:
Первый тест с использованием запросов и pytest
В качестве первого теста воспользуемся библиотекой запросов, чтобы вызвать вышеуказанный эндпойнт API и написать утверждение, проверяющее, что код HTTP-статуса равен 200:
- def test_get_locations_for_us_90210_check_status_code_equals_200():
- response = requests.get(«http://api.zippopotam.us/us/90210»)
- assert response.status_code == 200
Что тут происходит? В первой строчке теста мы вызываем метод get() из библиотеки запросов, чтобы выполнить запрос HTTP GET к определенной конечной точке, и сохраняем полный ответ в переменной response. Затем мы получаем свойство status_code из объекта ответа и пишем утверждение, используя ключевое слово pytest – assert, — проверяющее, что код статуса равен 200, как и ожидалось.
Вот и все о первом, и, конечно, очень простеньком тесте нашего API. Запустим этот тест и посмотрим, что будет. Я предпочитаю пользоваться командной строкой, потому что именно так мы будем запускать тесты, когда они станут частью автоматизированного процесса сборки. Мы можем сделать это, вызвав pytest и сообщив ему, где искать файлы тестов. Используя проект, ссылка на который дана в конце статьи, и предполагая, что мы находимся в корневой папке проекта, вызов
Даст в результате такую информацию в консоли:
Кажется, наш тест пройден. Так как я не доверяю тестам, падения которых не видел (и вам не советую), давайте изменим ожидаемый HTTP-код статуса с 200 на 201 и посмотрим, что будет.
Это изменение, как можно видеть, заставило тест упасть. Кажется, с этим тестом все в порядке.
Расширение тестового набора
Как правило, нас интересует не только HTTP-код статуса ответа. К примеру, давайте проверим, правильно ли значение заголовка Content Type идентифицирует тело ответа как формат JSON:
That makes our test fail, as you can see. It looks like we’re good to go with this one.
- def test_get_locations_for_us_90210_check_content_type_equals_json():
- response = requests.get(«http://api.zippopotam.us/us/90210»)
- assert response.headers[‘Content-Type’] == «application/json»
В объекте ответа заголовки доступны как словарь (список пар ключ-значение) headers, и получить значение определенного заголовка можно, передав правильный ключ (имя заголовка). Затем мы можем проверить полученное значение, используя утверждение pytest и ожидаемое значение ‘application/json‘.
Как насчет проверки элементов тела ответа? Давайте вначале убедимся, что элемент тела ответа country (см. пример ответа JSON выше) соответствует стране United States:
- def test_get_locations_for_us_90210_check_country_equals_united_states():
- response = requests.get(«http://api.zippopotam.us/us/90210»)
- response_body = response.json()
- assert response_body[«country»] == «United States»
Библиотека запросов поставляется со встроенным декодером JSON, которым можно пользоваться для получения тела ответа из объекта ответа и превращения его в настоящий JSON-объект. Декодер вызывается методом json(), который вернет ошибку ValueError, если тела ответа не существует, или в нем невалидный JSON.
После того, как мы трансформировали тело ответа в объект JSON, мы можем получить доступ к элементам тела, обращаясь к их имени – в этом случае country.
Для извлечения и проверки значения имени места для первого места в списке можно сделать что-то вроде этого:
- def test_get_locations_for_us_90210_check_city_equals_beverly_hills():
- response = requests.get(«http://api.zippopotam.us/us/90210»)
- response_body = response.json()
- assert response_body[«places»][0][«place name»] == «Beverly Hills»
Последний пример – давайте проверим, что список мест, возвращенных API, содержит ровно одну запись:
- def test_get_locations_for_us_90210_check_one_place_is_returned():
- response = requests.get(«http://api.zippopotam.us/us/90210»)
- response_body = response.json()
- assert len(response_body[«places»]) == 1
После конвертации тела ответа в JSON все это довольно просто. Метод len(), встроенный в Python, возвращает длину списка, в этом случае – количество элементов в массиве places в возвращенном API документе JSON.
В следующий раз мы рассмотрим создание управляемых тестов с использованием запросов и pytest.
Использование примеров
Примеры кода, использованного в статье, можно найти на моей странице GitHub. Скачав проект, запустите следующее (если вы правильно установили Python):
pip install -r requirements.txt
из корня проекта python-requests для установки нужных библиотек, и вы сможете запустить тесты самостоятельно.