// Every assertion type, every fuzzy marker, every match variant, every JS/Java interop pattern — with examples for each. The only Karate assertion reference you'll ever need.
Karate provides the most powerful built-in assertion engine of any API testing framework. No external libraries needed — no RestAssured, no Hamcrest, no AssertJ. Everything is built in.
> < >= <=) and boolean expressions only.
Feature: Assertion styles overview Scenario: Using match (preferred) Given url 'https://api.example.com/users/1' When method get Then status 200 And match response.name == 'John Doe' And match response.id == 1 And match response contains { role: 'admin' } Scenario: Using assert (numeric only) * assert responseStatus == 200 * assert response.age > 18 * assert response.items.length >= 1 Scenario: Using karate.expect() (Chai-style) * karate.expect(response.name).to.equal('John') * karate.expect(response.age).to.be.above(18) * karate.expect(response).to.have.property('email')
The foundation of all Karate assertions. match == performs intelligent equality that ignores key ordering and handles whitespace. match != asserts inequality.
# JSON object — key order doesn't matter * def user = { id: 1, name: 'John' } * match user == { id: 1, name: 'John' } * match user == { name: 'John', id: 1 } # Property-level match * match user.id == 1 * match user.name == 'John' # Arrays — order DOES matter * def nums = [1, 2, 3] * match nums == [1, 2, 3] # Match against variables * def expected = { id: 1, name: 'John' } * match user == expected # Strings * def msg = 'Hello World' * match msg == 'Hello World' # Numbers * match response.total == 42.5 # Booleans * match response.active == true # Null * match response.deletedAt == null
# Simple negative assertions * def user = { id: 123, status: 'active' } * match user != { id: 456 } * match user.status != 'inactive' * match user.id != 0 # Text not equals * def message = 'Success' * match message != 'Error' * match message != ''
# Assert value is within a range * def temp = 36.8 * match temp within { low: 36.0, high: 37.5 } # Assert value is OUTSIDE a range * def score = 85 * match score !within { low: 0, high: 50 }
Contains operations let you assert subsets without requiring exact equality. Six powerful variants cover every partial matching scenario.
# Object — only check specified keys * match response contains { id: 1, name: 'Bret' } # Array — check element exists * def tags = ['admin', 'verified', 'premium'] * match tags contains 'admin' # Multiple elements (order irrelevant) * match tags contains ['admin', 'verified']
* def user = { id: 1, name: 'John' } * match user !contains { deleted: true } * match user !contains { id: 456 } * def tags = ['admin', 'verified'] * match tags !contains 'suspended' * match tags !contains [4, 5]
* def nums = [1, 2, 3] * match nums contains only [3, 2, 1] * match nums contains only [2, 3, 1] # This FAILS — missing element 3: # * match nums contains only [1, 2]
* def tags = ['admin', 'verified', 'premium'] * match tags contains any ['admin', 'superuser'] # Works for objects too * def data = { a: 1, b: 'x' } * match data contains any { b: 'x', c: true }
* def data = { a: 1, b: 2, d: { x: 10, y: 20 } } * def expected = { a: 1, d: { y: 20 } } * match data contains deep expected # With nested arrays * def data2 = { arr: [{ b: 2, c: 3 }, { b: 4}] } * match data2 contains deep { arr: [{ b: 2 }] }
* def resp = { foo: ['a', 'b', 'c'] } # Order doesn't matter at any depth * match resp contains only deep { foo: ['c', 'a', 'b'] }
match each applies a validation pattern to every element in an array. Combine with fuzzy markers and contains for maximum power.
# Type-check every element * match each response == """ { id: '#number', name: '#string', email: '#string', active: '#boolean' } """ # Each element contains subset * match each response contains { id: '#number' } # Each element with custom validation * match each response == { id: '#number? _ > 0' } # Simple array of primitives * def tags = ['api', 'test', 'karate'] * match each tags == '#string' # Each with custom JS expression * def statusEnum = ['active', 'pending'] * match each response..status == '#? statusEnum.includes(_)'
# _$ references the parent object * def orders = """ [ { items: 3, total: 30, pricePerItem: 10 }, { items: 5, total: 25, pricePerItem: 5 } ] """ # Validate total == items * pricePerItem * match each orders contains { total: '#? _ == _$.items * _$.pricePerItem' } # match each contains deep * def items = """ [ { a: 1, arr: [{ b: 2, c: 3 }] }, { a: 1, arr: [{ b: 2, c: 3 }, { b: 4 }] } ] """ * match each items contains deep { a: 1, arr: [{ b: 2 }] }
| Symbol | Meaning | Context |
|---|---|---|
_ | Current value being validated ("self") | Any #? expression |
$ | JSON root document | Cross-field validation |
_$ | Parent object in iteration | match each only |
Validate data types and patterns without hardcoding exact values. These markers are what make Karate uniquely powerful — no other framework has this built in.
| Marker | Description | Example | Matches |
|---|---|---|---|
#ignore | Skip validation entirely for this field | match resp == { id: '#ignore' } | Any value |
#null | Value must be null (key MUST exist) | match resp == { v: '#null' } | { v: null } |
#notnull | Value must not be null | match resp == { v: '#notnull' } | { v: 'anything' } |
#present | Key must exist (any value, even null) | match resp == { v: '#present' } | { v: null } or { v: 1 } |
#notpresent | Key must NOT exist | match resp == { v: '#notpresent' } | { } (no key v) |
#string | Must be a string | match resp == { name: '#string' } | "John" |
#number | Must be a number | match resp == { age: '#number' } | 25, 3.14 |
#boolean | Must be true or false | match resp == { ok: '#boolean' } | true, false |
#array | Must be a JSON array | match resp == { items: '#array' } | [1,2,3] |
#object | Must be a JSON object | match resp == { addr: '#object' } | { street: '...' } |
#uuid | Must match UUID format | match resp == { id: '#uuid' } | "550e8400-e29b..." |
#regex STR | Must match regular expression | match resp == { email: '#regex .+@.+' } | "a@b.com" |
#? EXPR | Custom JS expression must return true | match resp == { age: '#? _ > 18' } | Any number > 18 |
#[N] | Array must have N elements | match resp == '#[10]' | Array of length 10 |
#[] SCHEMA | Array with optional element schema | match resp == '#[] #string' | Array of strings |
#(EXPR) | Embedded expression (substituted before match) | match resp == { v: '#(expected)' } | Value of expected variable |
# Field can be missing OR match the type * def user = { name: 'John', age: 30 } * match user == { name: '#string', age: '#number', bio: '##string', # missing = OK phone: '##string' # missing = OK } # ##null matches both missing AND null * def foo = { a: 1 } * match foo == { a: 1, b: '##null' } * def bar = { a: 1, b: null } * match bar == { a: 1, b: '##null' } # Works with any marker # ##string, ##number, ##object, ##array, etc.
# Key is MISSING entirely * def foo = { } * match foo == { a: '##null' } # PASS * match foo == { a: '#notpresent' } # PASS # Key EXISTS with null value * def bar = { a: null } * match bar == { a: '#null' } # PASS * match bar == { a: '#present' } # PASS # Key EXISTS with a value * def baz = { a: 1 } * match baz == { a: '#notnull' } # PASS * match baz == { a: '#present' } # PASS
Define schemas once, reuse them everywhere. Simpler and more powerful than JSON Schema, with zero dependencies.
Background: * def geoSchema = { lat: '#string', lng: '#string' } * def addressSchema = { street: '#string', suite: '#string', city: '#string', zipcode: '#regex \\d{5}-\\d{4}', geo: '#(geoSchema)' } * def companySchema = { name: '#string', catchPhrase: '#string', bs: '#string' } * def userSchema = """ { id: '#number', name: '#string', username: '#string? _.length >= 3', email: '#regex .+@.+\\..+', address: '#(addressSchema)', phone: '#string', website: '#string', company: '#(companySchema)' } """ Scenario: Validate single user Given url 'https://jsonplaceholder.typicode.com' And path 'users', 1 When method get Then status 200 And match response == userSchema Scenario: Validate all users Given url 'https://jsonplaceholder.typicode.com' And path 'users' When method get Then status 200 And match each response == userSchema
# Must be an array (any size) * match response == '#[]' # Exact array length * match response == '#[10]' # Array of strings * match response == '#[] #string' # Array of exactly 3 strings * match items == '#[3] #string' # Each element satisfies a predicate * match items == '#[]? _.length == 1' # Combine type + length + predicate * match items == '#[] #string? _.length > 0'
# Load schema from JSON file * def userSchema = read('classpath:schemas/user.json') * match response == userSchema # Or from a .feature file * def result = call read('classpath:common/schemas.feature') * match response == result.userSchema
# Range validation * match product == { price: '#number? _ > 0 && _ < 10000', discount: '#number? _ >= 0 && _ <= 100', quantity: '#number? _ > 0' } # String length validation * match user == { username: '#string? _.length >= 3', bio: '##string? _.length <= 500' } # External variable reference * def min = 1 * def max = 100 * match value == { count: '#? _ >= min && _ <= max' } # Enum validation * def validStatuses = ['active', 'pending', 'disabled'] * match user == { status: '#? validStatuses.includes(_)' } # Date format check * match resp == { created: '#regex \\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}' }
# $ references JSON root * def temperature = { celsius: 100, fahrenheit: 212 } * match temperature == { celsius: '#number', fahrenheit: '#? _ == $.celsius * 1.8 + 32' } # Embedded expression (cleaner syntax) * match temperature contains { fahrenheit: '#($.celsius * 1.8 + 32)' } # Validation functions * def isValidEmail = function(e) { return e.indexOf('@') > 0 } * match user.email == '#? isValidEmail(_)' # Complex function validation * def isISO8601 = function(d) { var parsed = new Date(d); return !isNaN(parsed.getTime()); } * match resp.createdAt == '#? isISO8601(_)'
| Variable | Type | Description |
|---|---|---|
response | JSON / XML / String | Auto-parsed response body |
responseStatus | int | HTTP status code (200, 404, etc.) |
responseTime | long | Response time in milliseconds |
responseHeaders | Map<List> | All response headers |
responseCookies | Map | Response cookies with metadata |
responseBytes | byte[] | Raw binary response body |
responseType | String | json, xml, or string |
requestTimeStamp | long | Java system time when request sent |
# Simple status assertion Then status 200 # Status with assert (for ranges) * assert responseStatus >= 200 && responseStatus < 300 # Status from a list * match [200, 201, 204] contains responseStatus # Response type check * match responseType == 'json' # Response body as string * match response == 'Health Check OK' * match response contains 'OK' # Response body size * assert responseBytes.length > 0 # Configure expected status (for negative tests) * configure responseStatusCheck = false When method get * match responseStatus == 404 * match response.error == 'Not Found'
# match header (case-insensitive shortcut) * match header Content-Type contains 'application/json' * match header Cache-Control == 'no-cache' * match header X-Request-Id == '#notnull' # Direct responseHeaders access # (headers are Map of Lists, use [0]) * match responseHeaders['Content-Type'][0] contains 'json' # Case-insensitive header lookup (recommended) * def ct = karate.response.header('content-type') * match ct contains 'json' # Assert header exists * match responseHeaders['X-Correlation-Id'] == '#notnull'
# Cookie assertions * match responseCookies.sessionId == '#notnull' * match responseCookies.sessionId.value == '#string' * match responseCookies.sessionId.path == '/' # Response time (SLA compliance) * assert responseTime < 2000 # Store time before next request resets it * def firstCallTime = responseTime When method get * assert responseTime < firstCallTime * 2 # Performance budget * match responseTime within { low: 0, high: 1500 }
response, responseTime, etc.) are reset after every HTTP call. Store values in def variables before making new requests.
All fuzzy markers and match variants work with XML responses. Karate handles XPath natively.
# XML with fuzzy markers * def xml = """ <root> <hello>world</hello> <id>123</id> </root> """ * match xml == """ <root> <hello>#string</hello> <id>#ignore</id> </root> """ # XML attribute matching * def xml2 = <item id="123" status="active">content</item> * match xml2 == <item id="#string" status="#ignore">content</item>
# XPath extraction and match * def name = karate.xmlPath(xml, '/root/hello') * match name == 'world' # SOAP response validation * match response == """ <soap:Envelope xmlns:soap="..."> <soap:Body> <GetUserResponse> <Name>#string</Name> <Email>#regex .+@.+</Email> <Id>#number</Id> </GetUserResponse> </soap:Body> </soap:Envelope> """ # Text matching (non-JSON, non-XML) * match response == 'Health Check OK' * match response contains 'OK' * match response !contains 'Error'
* def cat = """ { "name": "Billie", "kittens": [ { "id": 23, "name": "Bob" }, { "id": 42, "name": "Wild" } ] } """ # Wildcard [*] returns arrays * match cat.kittens[*].id == [23, 42] * match cat.kittens[*].name == ['Bob', 'Wild'] # Deep scan .. (matches at any depth) * match cat..name == ['Billie', 'Bob', 'Wild'] # JsonPath filter * def bob = get[0] cat.kittens[?(@.id==23)] * match bob.name == 'Bob' # Array index * match cat.kittens[0].id == 23 * match cat.kittens[1].name == 'Wild'
# get keyword for extraction * def names = get cat.kittens[*].name * match names == ['Bob', 'Wild'] # get[0] for first match * def first = get[0] cat.kittens * match first.id == 23 # $ shortcut for response * def ids = $response.users[*].id * match ids == [1, 2, 3] # $varName shortcut for other variables * def data = { users: [{ n: 'A' }, { n: 'B' }] } * def names = $data.users[*].n * match names == ['A', 'B'] # karate.jsonPath for dynamic paths * def path = '$.kittens[0].name' * def val = karate.jsonPath(cat, path) * match val == 'Bob'
Write complex assertion logic using inline JavaScript functions, karate object helpers, and functional transforms.
# Inline JS validation functions * def isValidEmail = function(e) { return e.indexOf('@') > 0 && e.indexOf('.') > e.indexOf('@') } * def isValidUUID = function(s) { var regex = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i; return regex.test(s) } * def isISO8601 = function(d) { return !isNaN(new Date(d).getTime()) } # Use in #? expressions * match user == { id: '#? isValidUUID(_)', email: '#? isValidEmail(_)', created: '#? isISO8601(_)' } # Multi-field validator * def isPositiveInt = function(n) { return typeof n === 'number' && n > 0 && n % 1 === 0 } * match each response == { id: '#? isPositiveInt(_)' }
# karate.filter — filter array then assert * def admins = karate.filter(response, function(x){ return x.role == 'admin' }) * assert admins.length > 0 # karate.map — transform then assert * def names = karate.map(response, function(x){ return x.name }) * match names contains 'John' # karate.distinct — unique values * def roles = karate.map(response, function(x){ return x.role }) * def unique = karate.distinct(roles) * match unique == ['admin', 'user', 'guest'] # karate.sort then assert order * def sorted = karate.sort(response, function(a, b){ return a.id - b.id }) * match sorted[0].id == 1 # karate.sizeOf * assert karate.sizeOf(response) == 10 # karate.keysOf / karate.valuesOf * def keys = karate.keysOf(response[0]) * match keys contains 'id' # karate.lowerCase (case-insensitive compare) * def lower = karate.lowerCase(response) * match lower.name == 'john doe' # karate.forEach with assertion side-effects * karate.forEach(response, function(item, i){ if (item.role == 'admin') karate.log('Admin:', item.name) })
Call Java classes directly from Karate for enterprise-grade custom validation, complex date parsing, crypto checks, and more.
# Call static Java methods * def UUID = Java.type('java.util.UUID') * def generated = UUID.randomUUID().toString() * match generated == '#uuid' # Timestamp validation with Java * def System = Java.type('java.lang.System') * def now = System.currentTimeMillis() * assert response.timestamp <= now # Date parsing and comparison * def sdf = Java.type('java.text.SimpleDateFormat') * def fmt = new sdf('yyyy-MM-dd') * def parsed = fmt.parse(response.date) * assert parsed != null # Case-insensitive string comparison * assert response.name.equalsIgnoreCase('JOHN DOE') # Regex via Java Pattern * def Pattern = Java.type('java.util.regex.Pattern') * def p = Pattern.compile('^[A-Z]{2}\\d{4}$') * assert p.matcher(response.code).matches()
// src/test/java/utils/AssertionUtils.java package utils; import java.util.*; public class AssertionUtils { public static boolean isValidPhone(String p) { return p.matches("^\\+?\\d{10,15}$"); } public static boolean isSorted(List<Integer> list) { for (int i = 1; i < list.size(); i++) { if (list.get(i) < list.get(i-1)) return false; } return true; } public static boolean containsKey( Map<String,Object> map, String key) { return map.containsKey(key); } }
* def Utils = Java.type('utils.AssertionUtils') * assert Utils.isValidPhone(response.phone) * assert Utils.isSorted(response.ids) # In #? expression * match each response == { phone: '#? Utils.isValidPhone(_)' } # Match class (programmatic Java assertions) // In Java code: // Match.that(response).contains("{ id: 1 }"); // Match.that(city).isEqualTo("London");
For teams migrating from Postman/Chai/Mocha, Karate v2 provides a familiar BDD assertion API. This is a convenience layer over match.
# Equality * karate.expect(response.name).to.equal('John') * karate.expect(response.name).to.not.equal('Jane') # Numeric comparisons * karate.expect(response.age).to.be.above(18) * karate.expect(response.age).to.be.below(100) * karate.expect(response.score).to.be.at.least(1) * karate.expect(response.score).to.be.at.most(100) # Property checks * karate.expect(response).to.have.property('email') * karate.expect(response).to.not.have.property('password') # Array/String includes * karate.expect(response.tags).to.include('active') * karate.expect(response.name).to.include('John') # Type checks * karate.expect(response.id).to.be.a('number') * karate.expect(response.name).to.be.a('string') * karate.expect(response.items).to.be.an('array') # Length * karate.expect(response.items).to.have.length(5) # Truthiness * karate.expect(response.active).to.be.ok * karate.expect(response.deleted).to.not.be.ok
# match style (idiomatic Karate) * match response.name == 'John' * match response contains { email: '#string' } * match response.tags contains 'active' * assert response.age > 18 # karate.expect style (Chai-like) * karate.expect(response.name).to.equal('John') * karate.expect(response).to.have.property('email') * karate.expect(response.tags).to.include('active') * karate.expect(response.age).to.be.above(18)
By default, a failed match stops the scenario immediately. Soft assertions collect all failures before stopping.
Scenario: Validate all fields at once # Enable soft assertions * configure continueOnStepFailure = true * match response.name == 'Alice' * match response.age == 30 * match response.role == 'admin' * match response.email contains '@' * match response.active == true # Disable — all 5 assertions checked, # failures collected, scenario fails at end * configure continueOnStepFailure = false
A comment (#) immediately before a match becomes the failure message label — making test reports human-readable.
Scenario: Validate user response * def user = { name: 'bar', status: 'pending' } # user name should be baz * match user.name == 'baz' # Error message includes: # "user name should be baz" # "match failed: EQUALS - not equal" # this comment is ignored # status must be active * match user.status == 'active' # Only the LAST comment before match is used
# Configure retry settings * configure retry = { count: 5, interval: 2000 } # Retry until status is 200 Given url 'https://api.example.com/job/123' And retry until responseStatus == 200 When method get # Retry until response field matches Given url 'https://api.example.com/job/123' And retry until response.status == 'complete' When method get # Complex JS expression And retry until response.status == 'done' || response.status == 'failed' When method get # Default: 3 attempts, 3000ms interval And retry until response.items.length > 0 When method get
Scenario: Wait for async job to complete # Start a background job Given url baseUrl + '/jobs' And request { type: 'export', format: 'csv' } When method post Then status 202 * def jobId = response.jobId # Poll until complete (max 10 tries, 3s apart) * configure retry = { count: 10, interval: 3000 } Given url baseUrl + '/jobs/' + jobId And retry until response.status == 'complete' || response.status == 'failed' When method get # Assert final state * match response.status == 'complete' * match response.downloadUrl == '#notnull'
Scenario: GraphQL assertion Given url 'https://api.example.com/graphql' # Use text keyword for GraphQL (not def) And text query = """ { user(id: 1) { id name email posts { title } } } """ And request { query: '#(query)' } When method post Then status 200 # Assert GraphQL response structure * match response.data.user == { id: '#notnull', name: '#string', email: '#regex .+@.+', posts: '#array' } # Assert no GraphQL errors * match response.errors == '#notpresent' # Assert nested array * match each response.data.user.posts == { title: '#string' }
Scenario: WebSocket message assertion # Connect to WebSocket * def handler = function(msg) { return msg.contains('COMPLETE') } * def ws = karate.webSocket(wsUrl, handler) # Send message * ws.send('{ "action": "subscribe" }') # Wait for matching message (timeout: 5s) * def result = ws.listen(5000) * match result contains 'COMPLETE' Scenario: gRPC assertion # gRPC uses same match assertions # Protobuf auto-converts to JSON Given url 'grpc://localhost:50051' And path 'myservice.MyMethod' And request { name: 'test' } When method post * match response == { message: '#string', code: '#number' }
# set — add or modify properties * def user = { id: 1, name: 'John' } * set user.active = true * set user.profile.email = 'john@test.com' * match user.active == true * match user.profile.email == 'john@test.com' # set multiple with table syntax * def user = {} * set user | path | value | | name.first | 'John' | | name.last | 'Doe' | | age | 30 | * match user.name.first == 'John' # remove — strip fields before assertion * def resp = response * remove resp.password * remove resp.internalId * match resp == expected # remove array element by index * def items = [1, 2, 3] * remove items[1] * match items == [1, 3] # Verify removal * match user.password == '#notpresent'
# delete — dynamic key removal * def key = 'tempData' * def obj = { id: 1, tempData: 'remove' } * delete obj[key] * match obj == { id: 1 } # karate.merge — combine then assert * def base = { id: 1 } * def extra = { name: 'John' } * def merged = karate.merge(base, extra) * match merged == { id: 1, name: 'John' } # karate.append — build arrays * def arr = karate.append([1], [2], [3]) * match arr == [1, 2, 3] # Transform XML to JSON for easier asserts * def json = karate.toJson(xmlResponse) * match json.root.name == 'John' # Remove XML element * def xml = <r><a>1</a><b>2</b></r> * remove xml /r/b * match xml == <r><a>1</a></r>
| Symbol | Equivalent | Usage in #() |
|---|---|---|
^ | contains | #(^expected) |
^^ | contains only | #(^^expected) |
^* | contains any | #(^*expected) |
^+ | contains deep | #(^+expected) |
!^ | not contains | #(!^expected) |
# Define expected schema * def required = { id: '#number', title: '#string' } # Response contains at minimum these fields * match response == '#(^required)' # Array contains all expected items (any order) * def expected = [{ id: 42 }, { id: 23 }] * match cat == { name: 'Billie', kittens: '#(^^expected)' } # Contains any of these * def data = { tags: ['api', 'test'] } * match data.tags == '#(^*["api", "other"])' # Not contains * match data.tags == '#(!^["deleted"])'
Copy-paste patterns for the most common API assertion scenarios.
# POST — Create * match responseStatus == 201 * match response.id == '#notnull' * match response contains request # GET — Read * match responseStatus == 200 * match response == schema # PUT — Update * match responseStatus == 200 * match response.updatedAt == '#notnull' # DELETE — 204 No Content * match responseStatus == 204
# Disable auto status check for error tests * configure responseStatusCheck = false # 400 Bad Request * match responseStatus == 400 * match response == { error: '#string', message: '#string' } # 401 Unauthorized * match responseStatus == 401 # 404 Not Found * match responseStatus == 404 * match response.error contains 'not found'
* match response == { data: '#[] #object', page: '#number', perPage: '#number', total: '#number', totalPages: '#number' } * assert response.data.length <= response.perPage * match each response.data contains { id: '#number' }
# Login response * match response == { access_token: '#string', refresh_token: '#string', token_type: 'Bearer', expires_in: '#number? _ > 0' } # JWT structure (3 dot-separated parts) * match response.access_token == '#regex [A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+'
# Verify ascending sort * def ids = $response[*].id * def sorted = karate.sort(ids, function(a,b){ return a - b }) * match ids == sorted # Or use Java interop * def Utils = Java.type('utils.AssertionUtils') * assert Utils.isSorted(ids)
# Hard SLA * assert responseTime < 2000 # Range SLA with match within (v2) * match responseTime within { low: 0, high: 1500 } # Conditional SLA by endpoint * def sla = (path == '/health') ? 500 : 3000 * assert responseTime < sla
Scenario Outline: Validate user <id> Given url baseUrl + '/users/' + <id> When method get Then status 200 * match response.name == <name> * match response.role == <role> Examples: | id | name | role | | 1 | 'Alice' | 'admin' | | 2 | 'Bob' | 'user' | | 3 | 'Carol' | 'guest' |
# common/validate-user.feature (@ignore) Scenario: * match user == userSchema * match user.email == '#regex .+@.+' # In your test: * def result = call read('common/validate-user.feature') { user: response }
# Field must NOT be in response * match response.password == '#notpresent' * match response.ssn == '#notpresent' # Array must NOT contain value * match response.roles !contains 'superadmin' # Object must NOT have field * match response !contains { debug: '#present' } # Empty array check * match response.errors == '#[0]'