воскресенье, 11 декабря 2022 г.

Как тестировать тело запроса (body) в REST API


Это отрывок из статьи Как тестировать методы REST API. Мне хочется, чтобы было возможность дать ссылку именно на тестирование тела сообщения)
 

Что мы тут тестируем:

  1. Правильное тело (пример)

  2. Различные параметры (обязательность, работа параметров)

  3. Бизнес-логика

  4. Ошибки: бизнес-логика

  5. Перестановка мест слагаемых

  6. Регистрозависимость

  7. Ошибки: не well formed xml / json

 


Правильное тело (пример из ТЗ)


Самое простое, что можно сделать — дернуть пример из документации, чтобы посмотреть, как метод вообще работает. А потом уже писать обвязку в коде.

Это пойдут делать тестировщики, получив от вас новый функционал. И это же сделает разработчик интеграции / другой пользователь API.

Отсюда вытекают следующие выводы:

а) Примеры в документации должны быть! Причем желательно не один, а на каждый интересный кейс. Чтобы на него точно обратили внимание!

б) Примеры тестируем в первую очередь, потому что именно их дернут первыми. И потом будут делать по аналогии.


Практика на примере Users

В Users есть только один пример:

{

    "email": "milli@mail.ru",

    "name": " Машенька",

    "password": "1"

}

Его и попробуем отправить!

Упс, ошибочка… Читаем её внимательно: 

" email milli@mail.ru уже есть в базе"

В целом логично, раз метод создает нового пользователя, может быть ограничение уникальности. Смотрим в ТЗ, и правда:

Имя и емейл должны быть уникальными

Значит, метод не идемпотентный… Нельзя просто взять пример из ТЗ и отправить не глядя.

Справка

Метод HTTP является идемпотентным, если повторный идентичный запрос, сделанный один или несколько раз подряд, имеет один и тот же эффект, не изменяющий состояние сервера. Корректно реализованные методы GET, HEAD, PUT и DELETE идемпотентны, но не метод POST. © developer.mozilla.org

Нужно взять пример и отредактировать его так, чтобы данные стали уникальными. Можно от руки дописать несколько циферок:

{

    "email": "milli5678@mail.ru",

    "name": " Машенька5678",

    "password": "1"

}

А можно использовать функционал Postman, который позволяет указать рандомные значения — динамические переменные. Пожалуй, мне хватит $randomInt. Но так как я отправляю запрос в JSON-формате, то надо обернуть переменную в фигурные скобки, иначе постман считает её как простой текст:

{

    "email": "milli{{$randomInt}}@mail.ru",

    "name": " Машенька{{$randomInt}}",

    "password": "1"

}

Сработало!

См также: Как понять, что мы отправляли, какую переменную? 

Пример проверили, отлично. Он рабочий, но не идемпотентный, так что его нужно скорректировать под себя. Документация НЕ неправильная, запрос рабочий, если его прогонять на пустой базе в первый раз.

Однако пользователи бывают разные. Они вполне могут скопипастить пример, отправить его, получить ошибку и прибежать в поддержку ругаться, не читая сообщение об ошибке — у вас плохой пример, он не работает. 

И чем больше у вас пользователей, тем больше таких вопросов будет. Заводить ли тут баг на правку документации? Можно предложить дописать в ТЗ как-то так:

Запрос (для проверки запроса исправьте имя пользователя и email, чтобы они были уникальными): ...

То есть заранее подсказываем, что надо изменить, чтобы запрос сработал AS IS. Можно было бы и пример с рандомными переменными постмана дать, но тут надо понимать, что через постман его будут дергать тестировщики и аналитики со стороны заказчика.

Разработчики же должны написать код, используя ваш пример. А они тоже любят копипастить))) И если дать пример, заточенный под постман, то к вам снова придут с вопросом, почему ваш пример не работает, но уже в коде. И тут опять или писать около примера, что “$randomInt — переменная Postman, она тут для того-то”, или всё же примеры оставить в покое.

Я не вижу особой проблемы в текущем описании, это не повод ставить баг на документацию. Пример нормальный и рабочий. А если принесет головную боль поддержке, тогда и замените.

В нашей доке всего 1 пример. Если бы их было больше, надо было бы вызвать все. Именно на примерах пользователи учатся работать с системой. Дернули, получили такой же ответ, изучили его, осознали, запомнили =)

Пример — это основной позитивный сценарий. И мы его почти проверили. Почему “почти”? Мы проверили, что система вернула в ответе «успешно создалась Машенька562», но точно ли она создалась? Может быть, разработчик сделал заглушку и пока метод в разработке, он всегда возвращает ответ в стиле “успешный успех”, ничего при этом не делая.

Так что идем и проверяем, есть ли наша Машенька в GUI — http://users.bugred.ru/. Ищем поиском — есть!

Проверяем все поля:

  • Емейл правильный, какой мы отправляли.

  • Автор «rest», что логично.

  • Дата изменения — сегодня, что тоже правильно.

Значит, пользователь правда создан! Хотя постойте… Я же выполняла не метод CreateUser, а doRegister. Его основная цель — не создать карточку, а зарегистрировать пользователя в системе. Просто при регистрации карточка автоматом создается, поэтому её тоже зацепили проверкой.

А как проверить, что регистрация прошла успешно? Правильно, попробовать авторизоваться!

Нажимаем «Войти» и вводим наши данные: milli754@mail.ru / 1

Вошли! Ура, значит, регистрация правда сработала. Вот теперь мы закончили с основным позитивным сценарием.


Различные параметры


На входе мы передаем какие-то параметры. И теперь нам нужно взять каждый параметр и проверить отдельно. Как проверяем:

  1. Обязательность

  2. Перестановка мест слагаемых

  3. Бизнес-логика

Про первые 2 пункта мы поговорим чуть позже, сейчас сосредоточимся на бизнес-логике.

Вот у вас есть параметры запроса. Как их проверять? Если вы впали в ступор сейчас, то просто представьте себе, что это не API, а GUI. Что у вас в интерфейсе есть форма ввода с этими полями? Как будете тестировать? Вот ровно так и тестируйте, выкинув разве что попытку убрать ограничение на клиенте (снять с поля maxlenght).


Практика на примере Users

По ТЗ входные параметры:

Имя параметра

Тип

Обязательный?

Описание

email

строка

да

email пользователя

name

строка

да

имя пользователя

password

строка

да

пароль

Тестируем каждый параметр в отдельности. И тут, как и в GUI, надо понимать:

  • Есть ли какие-то проверки у поля? Проверяется ли email по маске email-а? Имя выверяется по справочникам? Или это “просто строка, как прислали, так и сохранил?”

  • Куда данные идут дальше, где отображаются? В приветствии, в отчете, в личном кабинете, где?

  • Как они потом используются? На емейл придет письмо?

email

Вроде доп. проверок разработчик не делал, но точно я этого не знаю. Поле базовое, может есть прям во фреймворке какие-то проверки, или в интернете скопипастил… Так что тут стоит убедиться, что email корректный.

Можно взять за основу вот этот чек-лист, но часть проверок скомбинировать

  1. Корректный существующий email, куда может прийти почта — подставляем свой (начинаем всегда с корректного)

  2. Верхний регистр, цифры в имени пользователя и доменной части — TEST77@mail9.ru

  3. Email с дефисами и нижним подчеркиванием — test_user-1@mail_test-1.com

  4. Email с точками в имени пользователя и парой точек в доменной части — test.user@test.test.test.ru

А дальше идут интересные тесты, которые по идее должны падать, но упадут ли в Users, есть ли там такие проверки?

  1. Email без точек в доменной части — test@mailcom

  2. Превышение длины email (>320 символов)

  3. Отсутствие @ в email — testmail.ru

  4. Email с пробелами в имени пользователя — test user@mail.ru

  5. Email с пробелами в доменной части — test@ma il.ru

  6. Email без имени пользователя — @mail.ru

  7. Email без доменной части — test@

  8. Некорректный домен первого уровня (допустимо 2-6 букв после точки: .ru) — test@mail.urururururu

Помним, что имя должно быть быть уникальным при этом, потому что негативные тесты мы не смешиваем, только если в этом состоит сам тест “а что, если несколько полей сразу плохие”. Так что пример запроса:

{

    "email": "test@mailcom",

    "name": " Машенька{{$randomInt}}",

    "password": "1"

}

И сервер отвечает — некорректный емейл!

Так что проверки на емейл нужны, особенно — на некорректный.

name

Имя можно тестировать по разному. Если по нему определяется пол, тесты будут одни, если предлагаются подсказки, другие, а если это простая строка — третьи.

Так вот, в Users имя — это простая строка. Пол по ней не определяется, оно просто сохраняется в системе, разве что в правом верхнем углу отображается

Поэтому закапываться в “а теперь проверим мужское имя, и женское, и…” смысла нет.. Так что наша задача — проверить:

  • Что это правда простая строка и туда влезет всё, что можно

  • Максимальную длину изучить

Ну и начинаем с позитивного теста. Итого получаем:

  1. Простое имя типа “Ольга”

  2. Имя с разным регистром, разными буквами и спецсимволами (все спецсимволы можно перечислить) — если есть ограничение по длине, разобьем на разные тесты

  3. 1 000 символов — ищем верхнюю границу, если она есть. Заодно смотрим, как это выглядит в интерфейсе и корректируем тест.

  4. 1 000 000 символов — ищем технологическую границу

Обратите внимание на то, что мы вроде как тестируем API-метод, но после его выполнения лезем в графический интерфейс и проверяем, как там выглядит результат нашего запроса.

А всё почему? Потому что нет абстрактных методов, которые делают “ничего”, просто отправляются. Они все зачем-то нужны. В нашем случае — чтобы создать пользователя в системе. И важно понимать, а что будет потом с нашими данными? Будут ли они нормально отображаться в интерфейсе? Ведь если нет, то надо ставить ограничение на API-метод.

Поэтому помните, что API и GUI идут рука об руку, а не живут в разных мирах.

Password

На пароле тоже никаких ограничений нет — мы это знаем, потому что уже отправляли запрос с паролем “1”. Вот если бы были всякие “нужны заглавные буквы” и прочее, мы бы провели тесты “а если пароль ненадежный” и посмотрели на качество сообщения об ошибке, а так тесты будут как у имени:

  1. Обычный пароль типа 1

  2. Смесь из разных регистров, разных букв и спецсимволов — если есть ограничение по длине или составу, разобьем на разные тесты

  3. 1 000 символов — ищем верхнюю границу, если она есть. Заодно смотрим, как это выглядит в интерфейсе и корректируем тест.

  4. 1 000 000 символов — ищем технологическую границу




Бизнес-логика


После базового теста мы:

  1. Читаем каждое предложение из ТЗ, «Особенности использования» или как этот раздел у вас называется

  2. Продумываем, как его проверить: как позитивно, так и негативно.



Практика на примере Users

Читаем особенности использования:

Пользователь создается и появляется в системе.

Это мы уже проверили. Дальше:

Автор у него всегда будет «SOAP / REST», изменять его можно только через соответствующий-метод.

Автора тоже проверили, но только вот в ТЗ он указан капсом, а по факту создается в нижнем регистре. Это уже небольшой баг, скорее всего документации, так как некритично и проще доку обновить. Сделали заметочку / сами исправили доку, если есть доступ.

А дальше видим, что изменять только только через соответствующий метод. Ага, то есть если создали через REST, менять можно тоже только через REST, через SOAP нельзя. И наоборот. Это и проверим.

Для начала под Машенькой в ГУИ посмотрим, есть ли возможность отредактировать собственную карточку — нету. Ок. Открываем в SOAP Ui WSDL проекта — http://users.bugred.ru/tasks/soap/WrapperSoapServer.php?wsdl, и смотрим, каким методом можно вносить изменения.

Нам вполне подойдет UpdateUserOneField, он обновляет одно любое поле пользователя. А как понять, какие поля есть? Покопаться в документации. В Users описаны не все методы, но есть описание CreateUser, где можно взять названия полей. Допустим, хочу обновить кличку кошки — cat. Получается запрос:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:wrap="http://foo.bar/wrappersoapserver">

   <soapenv:Header/>

   <soapenv:Body>

      <wrap:UpdateUserOneField>

         <email>milli754@mail.ru</email>

         <field>cat</field>

         <value>Еночка</value>

      </wrap:UpdateUserOneField>

   </soapenv:Body>

</soapenv:Envelope>

Отправляем, хммммм:

Поле cat успешно изменено на Еночка у пользователя с email milli754@mail.ru

Проверяем в интерфейсе, находим карточку пользователя и жмем “Просмотр” (прямую ссылку давать бесполезно, база дропается каждую ночь, тут только самому создать и посмотреть). Значение обновилось:

Значит, условие из ТЗ не выполнено, можно ставить баг!

А мы пока читаем дальше:

Имя и емейл должны быть уникальными

Для проверки нам надо сделать неуникальным:

  1. имя

  2. емейл

  3. оба поля сразу

Можем взять за основу наш исходный запрос, который 1 раз создался:

{

    "email": "milli754@mail.ru",

    "name": " Машенька562",

    "password": "1"

}

И варьируем его

1. Уникальный емейл, дубликат имени

{

    "email": "milli{{$randomInt}}@mail.ru",

    "name": " Машенька562",

    "password": "1"

}

2. Уникальное имя, дубликат емейл

{

    "email": "milli754@mail.ru",

    "name": " Машенька{{$randomInt}}",

    "password": "1"

}

3. Дубликаты сразу имени и емейл

{

    "email": "milli754@mail.ru",

    "name": " Машенька562",

    "password": "1"

}

И смотрим на ответ. Понятно ли нам из сообщения об ошибке, что именно пошло не так? А статус-код соответствует типу ошибки?





API-часть проверок


Бизнес-логику проверили, а дальше мы уже идем по специфике API:

  • Перестановка мест слагаемых (заголовки, тело)

  • Регистрозависимость (заголовки, тело)

  • Well formed xml / json



Перестановка мест слагаемых

Это как раз особенность API, поэтому очень важно её проверить. Бизнес-логика и проверки “а что можно ввести в такое-то поле” одинаковы для GUI и API, а вот переставить поля местами в графическом интерфейсе не получится.

При этом в API это сделать проще простого. Мы ведь передаем пары “ключ-значение”. Что будет, если мы попробуем поменять их местами? 

В идеале ничего не случится, потому что принимающая система должна из запрос цеплять информацию именно по названию ключа, а не делать “select *” и потом говорить “для первого поля из запрос проверь то, для второго сделай это…”. 

Ведь потом изменится входной запрос и у нас вся интеграция сломается! А это нехорошо… Так что смотрим как система реагирует на перестановки.


Регистрозависимость

Регистронезависимость не появляется из воздуха, её делает разработчик. Поэтому и проверить “как ведет себя система” — тоже надо. И в заголовках, и в теле, и в параметрах URL —  вообще везде, где есть символы.


Ошибки в ответе от сервера

Всегда обязательно изучаем ошибки. Помимо ошибок в бизнес-логике есть ещё неправильно составленный запрос. То есть не well formed. Пробуем такой послать и смотрим на адекватность ошибки

См также:

Правила Well Formed XML

Правила Well Formed JSON


Практика на примере Users

По бизнес-логике мы уже прошли, теперь пойдем по API-части:


1. Перестановка мест слагаемых

В json пробуем перестановку:

{

    "name": " Машенька{{$randomInt}}",

    "email": "test{{$randomInt}}@mail.com",    

    "password": "1"

}

Ура, работает! А как насчет form-data? Тоже всё ок, это хорошо:


2. Регистрозависимость

Мы уже поняли, что в Users регистронезависимости может и не быть. Поэтому проверим на одном поле для начала. И не email, а то с ним может быть ложная уверенность в корректной ошибке:

{

    "email": "test{{$randomInt}}@mail.com",  

    "NAME": " Машенька{{$randomInt}}",   

    "password": "1"

}

Запрос не сработал, увы: "Параметр name является обязательным!"

Названия ключей регистрозависимы. Это не очень хорошо, хотя и некритично. Такой баг разработчик может не захотеть исправлять, “пусть присылают по документации”. Ну что же, тогда единственным аргументом будет потом количество обращений в поддержку. 

Но мы, по крайней мере, получили информацию по работе системы.


3. Well formed

У нас на входе json, смотрим его правила:

  1. Данные написаны в виде пар «ключ:значение»

  2. Данные разделены запятыми

  3. Объект находится внутри фигурных скобок {}

  4. Массив — внутри квадратных []

И пытаемся сломать. Вообще самая частая ошибка — это запятая после последней пары «ключ:значение». Мы обычно скопипастим строку из середины (вместе с запятой), поставим в конец объекта, а запятую удалить забудем. Получится как-то так:

{

    "email": "test{{$randomInt}}@mailcom",

    "name": " Машенька{{$randomInt}}",

    "password": "1",

}

Отправляем такой запрос. М-м-м-м, ответ как-то не очень:

Это постман мне настойчиво подсвечивает красным лишнюю запятую, а если вызов идет из кода и там подсветки нет, то как понять, что пошло не так? Из текста сообщения об ошибке. Только вот из такого текста разработчик очень долго будет угадывать, что не понравилось системе… Нехорошо, стоит завести баг.

Попробуем другие способы сломать формат:

— не в виде пар “ключ:значение”

{

    "email": "test{{$randomInt}}@mail.com",

    "name": " Машенька{{$randomInt}}",

    "password"

}

Ответ будет такой же — "Параметр email является обязательным!"

Заметьте, что если мы “сломаем” так email, будет ложное ощущение, что система работает хорошо и правильно дает подсказку. А на самом деле нет… 

Так что если уже замечали странности раньше, проверяем на другом поле — на пароле, а не емейле. И видим, что ошибка непонятная.

— данные не разделены запятыми

{

    "email": "test{{$randomInt}}@mail.com"

    "name": " Машенька{{$randomInt}}"

    "password": "1"

}

— объект в квадратных скобках, а не фигурных

[

    "email": "test{{$randomInt}}@mail.com",

    "name": " Машенька{{$randomInt}}",

    "password": "1"

]

Ответ везде одинаковый — "Параметр email является обязательным!"

Не очень хорошо, в ожидаемый результат чек-листа проверок мы запишем что-то более «правильное» =)) Например, ошибка 400 и сообщение "Не well formed json в запросе".


См также:

Как тестировать методы REST API — полная статья, не только про заголовки



PS — статья написана в помощь студентам моих курсов «Инженер по тестированию ПО» и «Тестирование REST API». Хотите практики? Приходите к нам =)


Комментариев нет:

Отправить комментарий