Karate - powerful and simple framework
for REST API automation testing
SlideShare: http://b.link/karate
Roman Liubun’ | 22 Feb 2019
Experience
15 years overall exp. at automation
5+ years as automation expert
25+ projects in 9 companies
Now
Automation expert |
Trainer |
Roman Liubun’
Lviv | Ukraine
About me
Description
R&D project
Modern technologies
Small team of “seniors”
Frequent changes
No requirements
Main automation challenges
BDD tests as requirements
Testing alongside with development
Project
BDD scripts layer
Test logic layer
Business logic layer
Core (CRUD) layer
Typical API
automation
framework
Agenda?
Karate | Cucumber | REST-assured
Re-usable tests
Using JS, Java interop
Assertions, schema validation
DDT
Parallel execution & reporting
Live coding...
Summary
Karate
Author: Peter Thomas
Current version: 0.9.1
Initial release: ~2 years ago
Popularity: 1600+ stars on GitHub
Url: https://github.com/intuit/karate
Written on Java
Less code == less bugs
Scenario: create, retrieve a pet
Given pet endpoint is up
And request where name is ‘Wolf’
When we send post request
Then status is 200
And returned JSON with id, name
Scenario: create, retrieve a pet
Given url ‘http://mysite.io/v2/pet’
And request { name: ‘Wolf’ }
When method post
Then status 200
And match response ==
{ id:‘#number’, name:‘Wolf’ }
+ steps
+ business logic
+ pojos
CucumberKarate
and … that’s all :)
Simpler code == less bugs
public void createPetTest() {
given()
.contentType(ContentType.JSON)
.body("{"name": "Wolf"}")
.when()
.post("http://mysite.io/v2/pet")
.then().statusCode(200)
.body("id",
Matchers.instanceOf(Integer.class))
.body("name", equalTo("Wolf"));
}
Scenario: create, retrieve a pet
Given url ‘http://mysite.io/v2/pet’
And request { name: ‘Wolf’ }
When method post
Then status 200
And match response ==
{ id:‘#number’, name:‘Wolf’ }
REST-assuredKarate
If you need complexity
Scenario: update the pet
* def id = 5;
* def petJson = call read(‘genUserJson.js’) id
Given url ‘http://mysite.io/v2/pet’
And request petJson
When method put
Then status 200
And match response == { id:‘#(id)’, name:‘#(petJson.name)’ }
genUserJson.js
function(id) {
var result = {};
Result.id = id;
result.name = 'MyPet' + id;
return result;
}
DRY
Scenario: update pre-defined pet
* def createdPet = call read(‘createPet.feature’)
Given url ‘http://mysite.io/v2/pet’
And request { id:‘#(createdPet.response.id)’, name:‘newPetsName’ }
When method put
Then status 200
What about Java?
# Calling Java method from API test
* def uniqueId = Java.type(’utils.NumberUtils’).getUniqueId();
* def uniqueName = ‘TestUser’ + uniqueId
And request { name: ‘#(uniqueName)’ }
# Calling API test from Java method
public PetPojo createPet() {
Map<String, Object> pet;
pet = Runner.runFeature(getClass(), "get-pet.feature", null, true);
ObjectMapper mapper = new ObjectMapper();
return mapper.convertValue(pet, PetPojo.class);
}
Assertion capabilities
Scenario: create and retrieve a cat with ID
Given url ‘http://petstore.mysite.io/v2/pet’
And request { name:’KORAT’, id:’a9f7a56b-8d5c-455c-9d13-808461d17b91’ }
When method post
Then status 200
And match response == { name:’#regex [A-Z]{5}’, id:’#uuid’ }
Schema validation
* def subNode = { price:‘#string’, name:‘#regex[0-9X]’ }
* def isValidTime = read(‘time-validator.js’)
When method get
Then match response ==
"""
{
id: ‘#regex[0-9]+’,
odd: ‘#(subNode)’,
data: {
countryId: ‘#ignore’,
countryName: ‘#string’,
status: ‘#number? _ >= 0’,
time: ‘#? isValidTime(_)’
}
}
"""
Markers
#ignore
#null
#notnull
#present
#notpresent
#number
#uuid
#array
#object
#string
##string
#? EXPR
#[NUM] EXPR
#regex STR
DDT
@regression
Scenario Outline: create <name> cat with age = <age>
Given url ‘http://mysite.io/v2/pet’
And request { name: ‘<name>’, age: ‘<age>’ }
When method post
Then status 200
And match response == { id:’#number’, name:’<name>’, age:‘<age>’ }
# the single cell in the table can be any valid karate expression:
# csv, js, js function, json, variable, XPath or JsonPath.
Examples:
| read(‘kittens.csv’) |
kittens.csv
name,age
Bob,1
Wild,5
Nyan,3
Parallel execution
@KarateOptions ( tags = { "@regression", "~@ignore" } )
public class RegressionTests {
@Test
public void regressionTests() {
Results results = Runner.parallel(getClass(), 5, "target/reports");
assertTrue(results.getErrorMessages(), results.getFailCount() == 0);
}
}
Reporting
Other cool features
JsonPath, XPath expressions
Reused for perf. testing (Gatling)
Embedded UI for step-by-step debug
Websocket support, async capability
GraphQL API testing capability
Built-in environment switcher
Integration with CI pipelines
API mock or test-doubles
Allure reporting support
Postman import (exp.)
… and many others
And...
Takeaways
PROS
Single layer tests
JSON, XML native support
Integration with Java, JS
Powerful assertion capabilities
Parallel execution
Great docs, demos, reporting
No programming skills are required
Many other features out of the box
CONS
Must learn a new DSL language
No “find usage”, auto renaming
No syntax error highlighting
Q&A Contacts
romlub
romlub
www.linkedin.com/in/romlub
SlideShare: http://b.link/karate

Karate - powerful and simple framework for REST API automation testing

  • 1.
    Karate - powerfuland simple framework for REST API automation testing SlideShare: http://b.link/karate Roman Liubun’ | 22 Feb 2019
  • 2.
    Experience 15 years overallexp. at automation 5+ years as automation expert 25+ projects in 9 companies Now Automation expert | Trainer | Roman Liubun’ Lviv | Ukraine About me
  • 3.
    Description R&D project Modern technologies Smallteam of “seniors” Frequent changes No requirements Main automation challenges BDD tests as requirements Testing alongside with development Project
  • 4.
    BDD scripts layer Testlogic layer Business logic layer Core (CRUD) layer Typical API automation framework
  • 5.
    Agenda? Karate | Cucumber| REST-assured Re-usable tests Using JS, Java interop Assertions, schema validation DDT Parallel execution & reporting Live coding... Summary
  • 6.
    Karate Author: Peter Thomas Currentversion: 0.9.1 Initial release: ~2 years ago Popularity: 1600+ stars on GitHub Url: https://github.com/intuit/karate Written on Java
  • 7.
    Less code ==less bugs Scenario: create, retrieve a pet Given pet endpoint is up And request where name is ‘Wolf’ When we send post request Then status is 200 And returned JSON with id, name Scenario: create, retrieve a pet Given url ‘http://mysite.io/v2/pet’ And request { name: ‘Wolf’ } When method post Then status 200 And match response == { id:‘#number’, name:‘Wolf’ } + steps + business logic + pojos CucumberKarate and … that’s all :)
  • 8.
    Simpler code ==less bugs public void createPetTest() { given() .contentType(ContentType.JSON) .body("{"name": "Wolf"}") .when() .post("http://mysite.io/v2/pet") .then().statusCode(200) .body("id", Matchers.instanceOf(Integer.class)) .body("name", equalTo("Wolf")); } Scenario: create, retrieve a pet Given url ‘http://mysite.io/v2/pet’ And request { name: ‘Wolf’ } When method post Then status 200 And match response == { id:‘#number’, name:‘Wolf’ } REST-assuredKarate
  • 9.
    If you needcomplexity Scenario: update the pet * def id = 5; * def petJson = call read(‘genUserJson.js’) id Given url ‘http://mysite.io/v2/pet’ And request petJson When method put Then status 200 And match response == { id:‘#(id)’, name:‘#(petJson.name)’ } genUserJson.js function(id) { var result = {}; Result.id = id; result.name = 'MyPet' + id; return result; }
  • 10.
    DRY Scenario: update pre-definedpet * def createdPet = call read(‘createPet.feature’) Given url ‘http://mysite.io/v2/pet’ And request { id:‘#(createdPet.response.id)’, name:‘newPetsName’ } When method put Then status 200
  • 11.
    What about Java? #Calling Java method from API test * def uniqueId = Java.type(’utils.NumberUtils’).getUniqueId(); * def uniqueName = ‘TestUser’ + uniqueId And request { name: ‘#(uniqueName)’ } # Calling API test from Java method public PetPojo createPet() { Map<String, Object> pet; pet = Runner.runFeature(getClass(), "get-pet.feature", null, true); ObjectMapper mapper = new ObjectMapper(); return mapper.convertValue(pet, PetPojo.class); }
  • 12.
    Assertion capabilities Scenario: createand retrieve a cat with ID Given url ‘http://petstore.mysite.io/v2/pet’ And request { name:’KORAT’, id:’a9f7a56b-8d5c-455c-9d13-808461d17b91’ } When method post Then status 200 And match response == { name:’#regex [A-Z]{5}’, id:’#uuid’ }
  • 13.
    Schema validation * defsubNode = { price:‘#string’, name:‘#regex[0-9X]’ } * def isValidTime = read(‘time-validator.js’) When method get Then match response == """ { id: ‘#regex[0-9]+’, odd: ‘#(subNode)’, data: { countryId: ‘#ignore’, countryName: ‘#string’, status: ‘#number? _ >= 0’, time: ‘#? isValidTime(_)’ } } """ Markers #ignore #null #notnull #present #notpresent #number #uuid #array #object #string ##string #? EXPR #[NUM] EXPR #regex STR
  • 14.
    DDT @regression Scenario Outline: create<name> cat with age = <age> Given url ‘http://mysite.io/v2/pet’ And request { name: ‘<name>’, age: ‘<age>’ } When method post Then status 200 And match response == { id:’#number’, name:’<name>’, age:‘<age>’ } # the single cell in the table can be any valid karate expression: # csv, js, js function, json, variable, XPath or JsonPath. Examples: | read(‘kittens.csv’) | kittens.csv name,age Bob,1 Wild,5 Nyan,3
  • 15.
    Parallel execution @KarateOptions (tags = { "@regression", "~@ignore" } ) public class RegressionTests { @Test public void regressionTests() { Results results = Runner.parallel(getClass(), 5, "target/reports"); assertTrue(results.getErrorMessages(), results.getFailCount() == 0); } }
  • 16.
  • 17.
    Other cool features JsonPath,XPath expressions Reused for perf. testing (Gatling) Embedded UI for step-by-step debug Websocket support, async capability GraphQL API testing capability Built-in environment switcher Integration with CI pipelines API mock or test-doubles Allure reporting support Postman import (exp.) … and many others
  • 18.
  • 19.
    Takeaways PROS Single layer tests JSON,XML native support Integration with Java, JS Powerful assertion capabilities Parallel execution Great docs, demos, reporting No programming skills are required Many other features out of the box CONS Must learn a new DSL language No “find usage”, auto renaming No syntax error highlighting
  • 20.