yaml + ejs + faker + ava + суперагент
Чтобы сдержать обещание клиентам, что наш веб-сервер никогда не сломается, мы щедро пишем все больше и больше кода, чтобы покрыть каждый уголок остальных API. Он становится даже больше, чем код самого веб-сервера. Эти жесткие коды наконец превратились в кошмар.
Я пытаюсь переписать свое старое тестирование API так, чтобы оно было удобнее в обслуживании. Я трачу большую часть времени на следование идеям, а не на жестком коде.
- Быть декларативным
- Принимает глобальные переменные
- Принимает сгенерированные поддельные данные
- Вложенные тесты
- Парсер (с именем chef) тестовых файлов
- Умный парсер
Быть декларативным
Декларативное тестирование является ключом к этому решению, оно значительно улучшает сопровождение тестирования. Этот файл объявляет HTTP и Утверждения.
{ "name": "allow anonym to query user" "method": "GET" "api": "/user" "headers": { "accept": "application/json" }, "query": { "id": 1 }, "assert": { "status": 200, "body": { "id": 1 } } ... }
Подождите, этот JSON содержит слишком много кавычек « и фигурных скобок {, что ухудшает читаемость. YAML кажется лучшим решением.
name: allow anonym to query user method: GET api: /user headers: accept: application/json query: id: 1 assert: status: 200 body: id: 1 ...
Кстати, YAML поддерживает привязку для уменьшения избыточного кода. Вы можете определить конфигурации сверху, которые можно будет повторно использовать позже.
user_id: &id 100 name: allow anonym to query user method: GET api: /user headers: accept: application/json query: id: *id assert: status: 200 body: id: *id ...
Принимает глобальные переменные
Я поместил все тесты, относящиеся к одному API, в один тестовый файл и получил:
api/users.yml api/me.yml api/wallets.yml ...
Но некоторые файлы YML имеют одинаковые конфигурации, такие как Авторизация, userId. Должен быть один способ вставки глобальных переменных в файлы тестирования. Эти тестовые файлы должны служить шаблонами, с помощью которых шаблонизатор может обрабатывать. Под рукой есть множество мощных шаблонизаторов, таких как EJS, Pug. Я взял EJS.
#api/me.yml user_id: &id <%-logined_user_id%> name: allow logined user to query me method: GET api: /me headers: accept: application/json authorization: <%-logined_user_authorization%> assert: status: 200 body: id: *id ...
Перед тем, как указанный выше файл был преобразован в JSON, он отображался с глобальными переменными.
ejs.render(text, { logined_user_id: 10, logined_user_authorization: "Bearer ..." })
Принимает сгенерированные поддельные данные
При тестировании API неизбежно загружать в API большое количество различного контента, такого как имя пользователя, аватар, адрес, сообщения и комментарии. Я выбираю faker, чтобы сгенерировать и отобразить их в файлах EJS.
#api/comment.yml user_id: &id <%-logined_user_id%> name: allow logined user to post comment method: POST api: /comments headers: accept: application/json authorization: <%-logined_user_authorization%> body: text: <%-comment_text%> assert: status: 200 ...
Faker генерирует случайный контент.
ejs.render(text, { logined_user_id: 10, logined_user_authorization: "Bearer ...", comment_text: faker.random.words() })
Вложенные тесты
Поскольку тесты зависят от другого теста, я вложил последний тест в предыдущий, что ясно показывает последовательность тестов. Вот пример для проверки успешности обновления с помощью более позднего запроса.
- name: test1 method: PATCH api: /me .. after: - name: test2 method: GET api: /me
Парсер (с именем chef) тестовых файлов
После написания наших файлов декларативного тестирования мне нужен синтаксический анализатор, чтобы подготовить это тестирование к любым библиотекам assert, таким как mocha, chai или ava, и отправить HTTP-запрос с любыми библиотеками, такими как superagent, "принести". Здесь я выбираю ava и superagent, и что нужно сделать повару:
- Прочитать все файлы YML
- Рендеринг файлов YML как шаблонов с глобальными переменными
- Плоские вложенные тесты в массив, ожидающий запуска
- Запустить тест с ava
- отправить HTTP-запрос
- Утверждать
Первая версия этого парсера выглядит так:
ava(name, async t => { let req = superagent(test.method, host + test.api); if (test.headers['Content-Type']) { req = req.set('Content-Type', test.headers['Content-Type']) } if (test.headers['Authorization']) { req = req.set('Authorization', test.headers['Authorization']) } if (test.query) { req = req.query(test.query) } if (test.body) { req = req.send(test.body) } ... other http configuration const res = await req t.is(res.status, test.assert.status) if (reqeust.body.id) { t.is(res.body.id, reqeust.body.id) } ... other ava asserts })
Умный парсер
Есть много жесткого кода для конфигурации HTTP и утверждений. Кажется, что обслуживание не улучшается. Мне нужно оптимизировать декларативное тестирование, чтобы оно было адаптивным к суперагенту и ava для уменьшения этого грязного кода.
#api/user.yml ... superagent: set: accept: application/json query: limit: 10 ava: is: status: 200 body.0.id: 1 ...
Идея очень проста: просто поместите контекст, функцию и параметры в файл YML.
# yml context: function: parameter
А парсер YML переводит их в вызов функции Javascript.
context.function(parameter)
Если эти функции нужно запустить по порядку. Вы можете немного изменить YML следующим образом:
# yml context: - function1: parameter: - function2: parameter
Суперагент имеет несколько функций, таких как set, query, send и т. Д., А ava имеет is , true, not и т. д. Независимо от того, что они имеют, файл настроек YML использует эти имена функций как ключи и параметры как значения. И парсер становится меньше, но умнее.
ava(name, async t => { let req = superagent(test.method, host + test.api); //superagent Object.keys(test.superagent).forEach(item => { req = req[item](test.superagent[item]); }); const res = await req //ava assert Object.keys(test.ava).forEach(func => { const asserts = test.ava[func] || {}; Object.keys(asserts).forEach(keypath => { try { t[func](valueInPath(res, keypath), asserts[keypath]); } catch (e) { t.fail(); } }); }); })
Теперь шеф-повар очень весело громит тестирование API. Что вы рассказываете о тестировании API?