-
-
Notifications
You must be signed in to change notification settings - Fork 397
What is Zerocode Testing
Table of Contents
- What is Declarative Testing
- Advantages of Declarative Testing
- Drawing a Simile
- YAML or JSON based test-scenarios
- Testing Without Writing Code
- Test Case Fields
- HelloWorld Examples (Try at home)
- Running the Tests using JUnit
- Both Declarative and Extensible
Here is some similar insight from IEEE-
We propose a software testing paradigm called declarative testing. In declarative testing, a test scenario focuses on what to accomplish rather than on the imperative details of how to manipulate the state of an application under test and verify the final application state against an expected state. Declarative testing is a test design paradigm which separates test automation code into conceptual Answer, Executor, and Verifier entities.
- According to Wikipedia -
In computer science, declarative programming is a programming paradigm—a style of building the structure and elements of computer programs—that expresses the logic of a computation without describing its control flow.
- According to IEEE -
Preliminary experience with declarative testing suggests that the modular characteristics of the paradigm may significantly enhance the ability of a testing effort to keep pace with the evolution of a software application during the application's development process.
-
Instead of writing code
how
to achieve the testing goals, we writewhat
to achieve in the test intentions i.e. the test input and the expectations -
Here the framework, behind the scene, handles the execution via necessary code to do the job for us e.g. API calls, DB executions, producing/consuming from Kafka topics etc
-
In this style we attempt to minimize or eliminate side effects by describing what the
test
must accomplish in terms of the business functionality, rather than describe how to accomplish it via programming or coding
That makes the test automation a lot easy and clean.
In the Declarative Style we don't need to write any of the below.
- The Http Client (or Kafka Client) calls for REST APIs
- Request payload parsing
- Response payload parsing
- Code for assertions/verifications e.g. comparing actual vs expected response
Declarative Style | Traditional Style |
---|---|
"url":"/api/v1/register/persons" |
Create an HttpClient object. Set the url to "/api/v1/register/persons" e.g. RequestBuilder.setUri(httpUrl);
|
"method": "POST" |
Set this POST operaton to the HttpClient object e.g. RequestBuilder.create(methodName).setUri(httpUrl);
|
"request": { ... } |
Parse the request payload and set to HttpEntity. e.g. HttpEntity httpEntity = EntityBuilder.create().setContentType(APPLICATION_JSON).setText(reqBody).build();
|
None. Nothing to do. | Parse the response to Java object or JSON String |
"verify": {JSON as-it-is} |
Compare the actual response against expected field by field. - Use multiple assertThat(...) . - Traverse through the response Object field by field - Or use JSON Path to extract value |
Display all the mismatches and fail the test(time saver) | Stop at first mismatch and fail the test(unwanted delay in getting feedback) |
Straight forward and easy | Step chaining is not straight forward |
To draw a simile, we can pay attention to how docker-compose works. In docker-compose
we tell the Docker-Compose
framework(in a YAML file) to spin up certain things at certain ports etc, and then, things are done for us by the framework.
That's declarative way of doing things
e.g. of a compose YAML
file
---
version: '2'
services:
zookeeper:
image: confluentinc/cp-zookeeper:5.0.1
environment:
ZOOKEEPER_CLIENT_PORT: 2181
ZOOKEEPER_TICK_TIME: 2000
kafka:
image: confluentinc/cp-kafka:5.0.1
depends_on:
- zookeeper
ports:
- 9092:9092
environment:
KAFKA_BROKER_ID: 1
KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
How neat and compact is that? Just think of it, for instance, if we had to write code/shell-scripts for the same repetitive tasks, how much hassle we would have gone through!
Example of a Zerocode YAML Test Scenario(more>>) is below.
---
scenarioName: As simple GET request response
steps:
- name: "find_match"
url: "/api/v1/search/persons"
method: "GET"
request:
queryParams:
lang: "Amazing"
city: "Lon"
verify:
status: 200
body:
exactMatches: true
name: "Mr Bean"
e.g.
That's the declarative way of validating an API what we discussed earlier
- Http(REST API and SOAP)
- Kafka (Produce, Consume RAW vs JSON)
- Java Function call e.g. DB SQL Executror
Scenario
means a Test-Scenario or an User-Journey or a Use-Case during test automation. It is represented in the following way.
"scenarioName": "Free text - Validate a POST and GET method for a customer"
loop
means the same Test-Scenario to be executed a number of times.
e.g.
"scenarioName": "Free text - A scenario"
"loop": 3
When this DSL flag is set to true
, the framework will go ahead and execute the subsequent steps in the scenario file.
"ignoreStepFailures": true
This is an optional flag and you can skip this or set to false to retain the default behavior.
REST endpoint or a SOAP end-point or a Kafka topic.
"url": "/api/v1/register/persons",
Or you can mention the FQDN with http or https with port
"url": "https://apphost.gov.uk/api/v1/register/persons",
See ahead examples on how you can point to a Kafka topic using this url
field.
REST end-point or SOAP end-point
All Http methods such as POST, PUT, GET, PATCH, DELETE
etc
"method": "POST",
Or when we need to call a Java function
"method": "executeSql",
Or
when we need to validate Kafka events
"operation": "produce",
or
"operation": "consume",
Note- method
and operation
are identical and can be used interchangeably. Preferably method
is used for http
calls and operation
is used for Kafka
calls.
(See Kafka DSLs below)
Retry
comes handy when the actual response doesn't match the expected values in certain use-cases.
"retry": {
"max": 5,
"delay": 2000
},
The above settings will retry maximum of 5
times with 2sec delay between the retries.
If one fo the retries goes success(meaning if the actual response matches the expected response), then the framework will stop retrying further and come out of that step marking the it as PASSED
.
Custom Log
This is an optional field which can be used when user want custom log for particular step.
"customLog": "custom message"
For REST end-point or SOAP end-point, request details with Headers and Body payload
"request": {
"body": {
"id": 1000,
"name": "Titan"
}
},
Or when we need to call a Java function with a SQL query as method parameter
"request": "select id, name from customers"
This DSL field can be used for sending query params to the HTTP endpoints.
"queryParams":{
"param1": "value1",
"param2": "value2"
}
which is equivalent to ?param1=value1¶m2=value2
Request with headers and body payload,
"request": {
"headers": {
"X-GOVT-TOKEN": "90945"
},
"body": {
"id": 1000,
"name": "Titan"
}
},
-
verifyMode
is STRICT or LENIENT
{
"verifyMode": "STRICT",
"verify":{
...
}
}
When we specify STRICT
mode, then the actual payload has to exactly match the expected payload.
LENIENT
is the default mode even if we do not mention it.
Verifications
and Assertions
are used for the similar purpose where,
-
verify
is mostly used forverification
of an implementation against a Spec -
assertions
is mostly used forvalidation
an implementation
For REST services, we need to put the expected response with response Status, Headers and Body payload.
Only status
validation
"verify": {
"status": 200
}
or
"verify": {
"status": 200
}
Or status
and payload id
assertions
Only status
assertion
"verify": {
"status": 200,
"body": {
"id" : 583231
}
}
Or partial
or full
payload assertions
"verify": {
"status": 200,
"body": {
"login" : "octocat",
"id" : 583231,
"type" : "User"
}
}
Or with response headers
details
"verify": {
"status": 200,
"headers":{
"Server":"sit2.hsbc.co.uk",
"X-HSBC-BANK":"$NOT.NULL" //<--- "$NOT.NULL" if value is undeterministic
},
"body": {
"login" : "octocat",
"id" : 583231,
"type" : "User"
}
}
For REST services or SOAP, we need to put the expected response with response Status, Headers and Body payload.
Only status
assertion
"verify": {
"status": 200
}
This is the payload, if present in the request
section, then passed to the REST endpoint.
This is the payload, if present in the response
section, then treated as expected response
from the REST endpoint.
"verify": {
"status": 200
"body": {
"login" : "octocat",
"id" : 583231,
"type" : "User"
}
}
We mention the Kafka topic name
"url": "kafka-topic:heathrow-inbound",
We need to mention produce
or consume
from/to a Kafka topic
"operation": "produce",
or
"operation": "consume",
We need to Produce or Consume to/from a Kafka topic,
- a
RAW
record
"request": {
"records": [
{
"key": "key-101",
"value": "Hello Kafka"
}
]
},
- a JSON record
"request": {
"recordType" : "JSON",
"records": [
{
"key": "key-101",
"value": {
"name" : "Jey"
}
}
]
},
Or while Consuming we can specify whether to commitSync
after consuming, recordType
as RAW or JSON etc.
"request": {
"consumerLocalConfigs": {
"recordType" : "JSON",
"commitSync": true,
"maxNoOfRetryPollsOrTimeouts": 3
}
},
For Kafka services, we can put the expected response with response Status, RecordMetadata.
Only status
assertion
"verify": {
"status": "Ok"
}
Or status
with recordMetadata
assertion while Producing
"verify": {
"status": "Ok",
"recordMetadata": "$NOT.NULL"
}
Or size
with records
assertion while Consuming
"verify": {
"size": 1,
"records": [
{
"key": 101,
"value": {
"name" : "Jey"
}
}
]
}
- Http examples are here in GitHub-Http
- Kafka examples are here in GitHub-Kafka
- Java Function Call examples are here in GitHub-Java
More About Kafka, DB, OAuth2, Http etc (Click to expand)
-
Many more HelloWorld examples, such as Spring boot app testing, Performance testing, Kotlin app testing etc.
The purpose of Zerocode lib is to make your API tests easy to write, easy to change, easy to share.
See the Table Of Contents for usages and examples.
For Kafka testing approach, visit this page Kafka-Testing Quick Start.
- All examples above run via Junit
@Test
annotation like below.
@TargetEnv("github_host.properties")
@RunWith(ZeroCodeUnitRunner.class)
public class JustHelloWorldTest {
@Test
@Scenario("helloworld/hello_world_status_ok_assertions.json")
public void testGet() throws Exception {
}
}
You point to any JSON file and run. Hosts details are in the
.properties
file by@TargetEnv
- Also you can run as a
Suite
pointing to the root of apackage
.
While Zerocode framework is light-weight and simple to write test intentions in JSON/YAML format, at the same time we can customize/extend it to add our own flavours.
For instance, we can add custom Http Headers to the entire test-suite or an individual test-case, automate OAuth2 secured APIs, or use our own flavour of Apache Kafka Client to deal with Kafka Brokers and much more stuff.
...making all these things super easy and straight forward.
Visit the Zerocode Documentation Site for all things.
-
User's Guide
-
Matchers
-
Zerocode Value Tokens
-
YAML DSL
-
Http Testing
-
Kafka Testing
- Introduction
- Produce, consume proto message
- Produce raw message
- Consume raw message
- Produce JSON message
- Consume JSON message
- Produce and consume XML message
- Kafka - consume the latest message or n latest messages
- Produce avro message
- Consume avro message
- KSQL in action
- Produce multiple records
- Produce from file
- Produce to a partition
- Produce and consume records with headers
- Produce n assert partition ack
- Comsume and dump to file
- commitSync vs commitAsync
- Overriding config inside a test
- Chosing String or Int or Avro Serializer
- Chosing String or Int or Avro Deserializer
- Attaching timestamp during load
- Default timestamp provided by Kafka
- Consume and assert avro schema metadata
- Error handling - produce via avro schema
- Sorting Kafka records consumed
-
DB Testing
-
Kotlin Testing
-
Performance Testing - Load and Stress
- Performance Testing - via awesome JUnit runners
- Load Vs Stress generation on target application
- Run a single test or a scenario in parallel
- Run multiple test scenarios in parallel - Production load simulation
- Dynamically change the payload for every request
- Analytics - Useful report(s) or statistics
-
Parameterized Testing
-
Docker
-
More+
-
Extensions
-
JUnit5 Jupiter Test
-
Questions And Answers(FAQ)
- What is Zerocode testing?
- SSL http https connections supported?
- How to assert array size Greater-Than Lesser-Than etc?
- How to invoke POST api?
- How to assert custom headers of the response?
- How to pass custom security token into the request header?
- When to use JUnit Suite runner and when Zerocode Package runner?
- How to execute DB SQL and assert?
- How to handle Http response other than utf-8 e.g. utf-16 or utf-32 ?
- Random Number Generator Placeholders Usages and Limits
- Automation tests for Zerocode lib itself
- Picking a leaf value from the array matching JSON Path
- Array assertions made easy, incl. size and element finder
-
Read Our Blogs
- Top 16 Open Source API Testing Tools For REST & SOAP Services - joecolantonio (Lists popular tools - Globally)
- OAuth2 Test Automation - DZone 2min Read
- Zero defect APIs - Build Pipe Line - Medium 10 min Read
- Develop ZeroDefect API's with ZeroCode! - Extreme Portal ( A must read for all developers and test engineers) 10min Read
- Performance testing using JUnit and maven - Codeproject 10 min Read
- REST API or SOAP End Point Testing - Codeproject 10min Read
- DZone- MuleSoft API Testing With Zerocode Test Framework - DZone 5min Read
- Testing need not be harder or slower, it should be easier and faster - DZone 5 min Read
- Kotlin Integration Testing simplified via Zerocode - Extreme portal 10 min Read
- and More...