Что пишут в блогах

Подписаться

Онлайн-тренинги

Конференции

TestCon Moscow 2021
Конференция по тестированию и обеспечению качества ПО

7-9 сентября, Онлайн

Heisenbug 2021 Moscow
Большая техническая конференция для тестировщиков
5-7 октября 2021, онлайн

Что пишут в блогах (EN)

Разделы портала

Про инструменты

.
Создание тестов для GraphQL API на Python с использованием requests
15.07.2021 00:00

Автор: Баз Дейкстра (Bas Dijkstra)
Оригинал статьи
Перевод: Ольга Алифанова

На прошлой неделе я проводил двухдневную версию своего мастер-класса по тестированию API на Python. Пока мы выполняли упражнения по созданию автотестов для REST API с использованием requests, один из участников спросил, применимо ли это к GraphQL API. Никогда раньше этого не делал, но после совместного гугления и обсуждения мы постановили, что это небольшая проблема.

Так как мы обсуждали это в первый день курса, я обещал подготовить ряд быстрых упражнений, над которыми можно будет поработать на второй день. В этой статье вы увидите ряд примеров, которые входили в эти упражнения.

Один из участников предложил отличное приложение в качестве тестируемого API: SpaceX GraphQL API. Этот публичный API содержит информацию о компании SpaceX, а также ее истории, космическом флоте, миссиях, запусках, и т. д. Множество данных, есть с чем поработать.

Если вы не знакомы с GraphQL API, то они отличаются от обычных REST API способом доступа к данным. В обычных REST API вы, как правило, просто отправляете GET-запрос, чтобы получить данные от конкретного ресурса. К примеру, GET к адресу http://api.zippopotam.us/us/90210 вернет геолокацию, относящуюся к индексу США 90210.

В отличие от этого, в запросах GraphQL вы создаете GraphQL-запрос и отправляете его к API через POST. Это позволяет получать данные от нескольких ресурсов (потенциально – от множества источников данных) единым, агрегированным API-вызовом.

К примеру, если мы хотим получить название компании SpaceX, а также имена ее генерального директора и исполнительного директора, то это можно сделать, создав GraphQL-запрос к https://api.spacex.land/graphql/:

{
    company {
        ceo
        coo
        name
    }
}

Это вернет ответ с JSON-телом:

{
    "data": {
        "company": {
            "ceo": "Elon Musk",
            "coo": "Gwynne Shotwell",
           "name": "SpaceX"
        }
    }
}

Как можно видеть, сам запрос использует специфический синтаксис GraphQL, но ответ приходит на чистом JSON. Это хорошая новость, если вы планируете использовать библиотеку вроде requests для тестирования GraphQL API. Посмотрим, как это сделать.

Наиболее прямолинейный способ создания и отправки GraphQL-запроса к API – это жестко закодировать запрос в коде теста как строку (из нескольких абзацей):

query_company_ceo_coo_name = """
{
    company {
        ceo
        coo
        name
    }
}
"""

Теперь все, что нам нужно для отправки запроса к GraphQL API – это создать JSON-тело запроса с одноэлементным запросом, где наша строка запроса – это значение. Вот и все. Ниже – пример, как это выглядит в тесте с использованием requests и pytest, вместе с проверкой статус-кода ответа – чтобы убедиться, что наш запрос успешно обработан:

def test_retrieve_graphql_data_should_yield_http_200():
    response = requests.post("https://api.spacex.land/graphql/", json={'query': query_company_ceo_coo_name})
    assert response.status_code == 200

Так как тело ответа – это обычный JSON, мы можем проверять отдельные значения элементов тела ответа, конвертируя JSON в словарь Python методом json(), а затем получая нужный элемент и проверяя его значение. Ниже – пример, проверяющий, что генерального директора Space X зовут Илон Маск.

def test_retrieve_graphql_data_should_yield_ceo_elon_musk():
    response = requests.post("https://api.spacex.land/graphql/", json={'query': query_company_ceo_coo_name})
    response_body = response.json()
    assert response_body['data']['company']['ceo'] == 'Elon Musk'

Это на удивление самоочевидно. Единственное, что меня беспокоит – это жесткое кодирование запросов GraphQL как строк. Безусловно, должен существовать способ программно создавать и отправлять эти запросы, правда же? Рад, что вы спросили – да, такой способ есть. Но нам понадобится дополнительная библиотека.

sgqlc – это клиент GraphQL, позволяющий определять классы, представляющие наши GraphQL-запросы. Затем эти классы можно передавать в GraphQL API так же, как и в предыдущих примерах. Попробуем создать вот такой GraphQL-запрос – получение ID, названия и описания различных миссий SpaceX:

{
    missions {
        id
        name
        description
    }
}

Для этого мы сначала создадим класс, представляющий индивидуальный узел миссии:

from sgqlc.types import String, Type
 
class MissionNode(Type):
     id = String
    name = String
    description = String

Нас интересует три поля – id, название и описание. Значение каждого из этих полей – это String.

После этого создадим наш запрос из MissionNode:

from sgqlc.types import Field
 
class Query(Type):
    missions = Field(MissionNode)

Вот и все. Теперь у нас есть структура данных, которую можно использовать для создания GraphQL-запроса и передачи его в API. Вот как использовать ее в тестах:

def test_create_graphql_query_programmatically():
    query = Operation(Query)
    query.missions()
 
    response = requests.post("https://api.spacex.land/graphql/", json={'query': str(query)})
    assert response.status_code == 200
    assert len(response.json()['data']['missions']) == 10

Вначале мы создали новую копию нашего класса Query через query = Operation(Query). Затем мы добавили поля MissionNode, используя query.missions(), создав именно тот GraphQL-запрос, который мы хотим передать через POST. Теперь нам осталось только добавить этот объект как JSON-приложение к POST-запросу, и можем ехать. Не забудьте отправить строковую репрезентацию запроса, используя str() – не отправляйте сам объект, это не сработает!

Вот и все. Как видите, создание тестов для GraphQL API при помощи Python и requests не сильно отличается от тестирования обычных REST API. Все примеры из статьи можно найти на моей странице GitHub.

Обсудить в форуме