Karate - это инструмент тестирования API с открытым исходным кодом, разработанный Питер Томас из Intuit. Karate построен на основе HttpClient и Cucumber и имеет собственный DSL, чтобы упростить тестирование API. Несмотря на то, что он существует уже почти год, он очень быстро развился и обладает всеми возможностями, ожидаемыми от инструмента тестирования API.
Поскольку каратэ находится поверх огурца, оно наследует все функции огурца, поэтому вы можете писать тесты API в простом формате Given When Then и использовать все ключевые слова огурца, такие как Feature, Scenario Outline, Scenario, Примеры, Feature tagging.
Я создал эту шпаргалку, чтобы помочь всем, кто участвует в тестировании API, с примерами использования инструмента карате.
Пожалуйста, обрати внимание , эта шпаргалка - лишь верхушка айсберга. Каратэ имеет много других особенностей, которые здесь не упоминаются. Этот список - всего лишь наиболее распространенные операции, которые обычно используются при тестировании API.
UTF-8
3.7.0
1.8
1.8
1.8
0.8.0.RC4
3.13.0
com.intuit.karate
karate-core
${karate.version}
com.intuit.karate
karate-apache
${karate.version}
test
com.intuit.karate
karate-testng
${karate.version}
net.masterthought
cucumber-reporting
${cucumber.reporting.version}
test
Вы можете организовать и структурировать свой проект maven следующим образом:
Здесь вы можете создавать переменные, имеющие глобальную область видимости. Каратэ читает этот файл перед выполнением любого сценария. Это очень удобно при переключении сред, в которых конкретные переменные используются для разных сред.
function() {
var env = karate.env; // get java system property 'karate.env'
karate.log('karate.env selected environment was:', env);
karate.configure('ssl', true)
if (!env) {
env = 'dev'; //env can be anything: dev, qa, staging, etc.
}
var config = {
env: env,
AM_USERNAME: 'devuser',
AM_PASSWORD: 'devpass',
AM_HOST: 'https://am.'+env+'.example.net',
AM_AUTHENTICATE_PATH: '/am/json/realms/root/authenticate',
IDM_USERNAME: 'devuser',
IDM_PASSWORD: 'devpass',
IDM_HOST: 'https://idm.'+env+'.example.net',
IDM_MANAGED_USER_PATH: '/idm/managed/user',
};
if(env == 'qa') {
config.AM_USERNAME: 'myUserName'
config.AM_PASSWORD: 'myPa55word'
}
if(env == 'live') {
config.AM_USERNAME: 'admin'
config.AM_PASSWORD: 'secret'
}
karate.log('OpenAM Host:', config.AM_HOST);
karate.configure('connectTimeout', 60000);
karate.configure('readTimeout', 60000);
return config; }
@FR Feature: AM Admin Login
Scenario: Login as Admin to AM and get token
Given header X-OpenAM-Username = AM_USERNAME
Given header X-OpenAM-Password = AM_PASSWORD
Given url AM_HOST + AM_AUTHENTICATE_PATH
And request ''
When method POST
Then status 200
* assert response.tokenId != null
* def tokenId = response.tokenId
В приведенном выше примере AM_USERNAME, AM_PASSWORD, AM_HOST и AM_AUTHENTICATE_PATH берутся из karate-config.js
файл.
' 'Можно интерпретировать как любое из 'Дано', 'Когда', 'Тогда', 'И', но когда действие не обслуживает контекст, мы можем использовать ' '.
’+’ Действует как оператор конкатенации
В приведенном выше примере отправляется пустой запрос тела сообщения. Мы можем просто использовать ‘‘
Метод может быть любым допустимым HTTP-глаголом (Get, Post, Put, Patch, Delete).
' def ’Используется для хранения значения в переменной.
заголовок , url , запрос , метод , статус , отклик все ключевые слова карате образуют DSL. Чтобы увидеть полный список ключевых слов, посетите Intuit.
В приведенном выше примере ответ имеет формат JSON, поэтому мы можем использовать встроенную в карате нотацию JsonPath для анализа ответа.
Feature: request chaining with multiple api calls Scenario: chain request demo
* json req = read('classpath:com/example/templates/idm/create-user-template.json')
* def user = req.givenName
Given header X-Username = 'anonymous'
Given header X-Password = 'anonymous'
Given url AM_HOST + '/some/endpoint
And request ''
When method POST
* def authId = response.authId
* def payload1 =
'''
{'authId':'${authId}','callbacks':[{'type':'NameCallback','output':[{'name':'prompt','value':'Email Address'}],'input':[{'name':'IDToken0','value':'${user}@putsbox.com'}]}]}
'''
* replace payload1
| token
| value |
| ${authId} | authId |
| ${user} | user |
* json mypayload1 = payload1
Given header X-Username = 'anonymous'
Given header X-Password = 'anonymous'
Given url AM_HOST + '/openam/some-other-endpoint
And request mypayload1
When method POST
В приведенном выше примере выполняется первый вызов, и authId анализируется из ответа и сохраняется в переменной authId. Затем мы заменяем вторую полезную нагрузку на authId, полученный при первом вызове. Затем мы используем новую полезную нагрузку для отправки следующему вызову API.
Мы можем сделать наши сценарии повторно используемыми и вызывать их из других файлов функций. В этом примере мы можем создать «общий» файл create-user.feature, в котором мы можем отправить запрос на создание пользователя, но с другим телом запроса.
Feature: Create User in IDM
Scenario: Create user in IDM with given guid
Given header X-Requested-With = 'Swagger-UI'
Given header X-OpenIDM-Username = IDM_USERNAME
Given header X-OpenIDM-Password = IDM_PASSWORD
Given url IDM_HOST + IDM_MANAGED_USER_PATH
And request __arg
When method POST
Then status 201
Обратите внимание, что в приведенном выше примере мы используем __arg в качестве запроса тела сообщения.
Затем мы можем вызвать указанный выше файл функций и передать требуемое тело сообщения, которое, в свою очередь, мы можем прочитать из шаблона.
Feature: Create a user
Scenario: Create user in IDM
* json myReq = read('classpath:com/example/templates/idm/idm-create-user-template.json')
* call read('classpath:com/example/idm/idm-create-user.feature') myReq
Приведенный выше код считывает шаблон, который находится в расположении com/example/templates/idm/idm-create-user-template.json
и сохраняет его как переменную JSON с именем myReq
Затем мы можем отправить переменную JSON в другой файл функции, используя метод call.
Шаблон выглядит как
{
'mail' : 'david@putsbox.com',
'givenName' : 'david',
'sn' : 'putsbox',
'jobRole' : 'developer',
'telephoneNumber' : '91234567890',
'dob' : '01/02/2010', }
Мы можем прочитать конкретную переменную в вызываемом файле функций, который передается из вызывающего файла функций.
Feature: Create User in IDM
Scenario: Create user in IDM with given guid
Given header X-Requested-With = 'Swagger-UI'
Given header X-OpenIDM-Username = IDM_USERNAME
Given header X-OpenIDM-Password = IDM_PASSWORD
Given url IDM_HOST + IDM_MANAGED_USER_PATH
And request __arg.emailAddress
When method POST
Then status 201
Обратите внимание: в приведенном выше примере мы используем __arg.emailAddress в качестве запроса тела сообщения. Нас интересует только отправка адреса электронной почты в качестве запроса
Затем мы можем вызвать указанный выше файл функций и передать требуемое тело сообщения, которое, в свою очередь, мы можем прочитать из шаблона.
Feature: Create a user
Scenario: Create user in IDM
* json myReq = read('classpath:com/example/templates/idm/idm-create-user-template.json')
* json emailAddress = '{'emailAddress': '' +myReq.mail+ ''}'
* call read('classpath:com/example/fr/idm/idm-create-user.feature') emailAddress
Приведенный выше код извлекает поле почты из шаблона JSON. Когда мы передаем переменную в другой файл функций, она должна иметь тип JSON, поэтому переменная emailAddress должна быть допустимым JSON.
Затем мы можем отправить переменную JSON в другой файл функции с помощью метода call и отправить переменную JSON, в данном случае emailAddress
.
Мы можем выполнять сценарии в файле функций с помощью maven (что полезно для запуска тестов в среде CI)
import com.intuit.karate.cucumber.CucumberRunner; import com.intuit.karate.cucumber.KarateStats; import cucumber.api.CucumberOptions; import net.masterthought.cucumber.Configuration; import net.masterthought.cucumber.ReportBuilder; import org.apache.commons.io.FileUtils; import org.testng.annotations.Test; import java.io.File; import java.util.ArrayList; import java.util.Collection; import java.util.List; import static org.testng.AssertJUnit.assertTrue; @CucumberOptions(tags = {'@FR', '~@ignore'}) public class TestRunner_FR {
@Test
public void testParallel() {
String karateOutputPath = 'target/cucumber-html-reports';
KarateStats stats = CucumberRunner.parallel(getClass(), 1, karateOutputPath);
generateReport(karateOutputPath);
assertTrue('there are scenario failures', stats.getFailCount() == 0);
}
private static void generateReport(String karateOutputPath) {
Collection jsonFiles = FileUtils.listFiles(new File(karateOutputPath), new String[] {'json'}, true);
List jsonPaths = new ArrayList(jsonFiles.size());
for (File file : jsonFiles) {
jsonPaths.add(file.getAbsolutePath());
}
Configuration config = new Configuration(new File('target'), 'YOUR PROJECT NAME');
config.addClassifications('Environment', System.getProperty('karate.env'));
ReportBuilder reportBuilder = new ReportBuilder(jsonPaths, config);
reportBuilder.generateReports();
} }
Приведенный выше код запускает все файлы функций, помеченные как «@FR», но игнорирует все тесты, помеченные как «@ignore».
Он также создает отчет по огурцу для визуализации результатов тестовых прогонов.
mvn clean test -DargLine='-Dkarate.env=staging' -Dtest=TestRunner_FR
Здесь мы запускаем класс TestRunner_FR и устанавливаем среду как промежуточную.
В файле функций у нас также есть возможность выполнять javascript
Feature: Generate a random session id
Scenario: generate random session id
* def random_string =
'''
function(s) {
var text = '';
var possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
for (var i = 0; i < s; i++)
text += possible.charAt(Math.floor(Math.random() * possible.length));
return text;
}
'''
* def sessionId = random_string(10)
Приведенный выше код генерирует случайную строку длиной 10 и сохраняет ее в переменной с именем sessionId.
Поскольку каратэ находится на вершине огурца, тестирование на основе данных по умолчанию.
Feature: Data driven testing example Scenario Outline: An 'Invalid input request' error is returned if required parameters have incorrect values.
* def attribute_name = ''
* xml malformed_request =
* json activate_request = malformed_request
* def activate_response = call read('activate.feature') activate_request
* match activate_response.contentType == 'text/xml;charset=ISO-8859-1'
* match activate_response.gas_version == '5.2.7'
* match activate_response.error_code == '1000'
Examples:
| name_attribute | method_call
|
| auth_method
| Java.type('com.example.StringUtil').removeNodeByAttribute(xml_req, attribute_name) |
| app_url
| Java.type('com.example.StringUtil').removeNodeByAttribute(xml_req, attribute_name) |
В приведенном выше примере используются ключевые слова «Схема сценария» и «Примеры» Cucumber для создания тестов на основе данных. Для чтения каждого параметра мы используем угловые скобки
package com.example; public class StringUtil {
public static String getNumberFromString(String text) {
return text.replaceAll('\D+', '');
} }
Feature: Call java demo Scenario: Get number from text
Given url 'https://preview.putsbox.com/p/david/last.json'
When method GET
* def emailText = response.text
* def otpCode = Java.type('com.example.StringUtil').getNumberFromString(emailText)
* print otpCode
Приведенный выше файл функций вызывает метод Java в классе с именем StringUtil
. Затем сохраняет ответ на этот вызов в переменную otpCode.