Integrating API Testing with Keploy for Your Projects

Introduction⌗
Keploy is an open-source API Testing tool developed in Go that can record API request logs during local development and generate test cases. The generated test cases include request and response parameters as well as Mock configurations, allowing you to easily integrate these cases into your existing CI/CD Pipeline, thereby further improving code test coverage in CI/CD!
Supported languages:
- Go
- C#
- Rust
- Java
- Python
- JavaScript
Backend data dependencies supported for mocking:
- HTTP
- MySQL
- Redis
- MongoDB
- DynamoDB
- PostgreSQL
Architecture⌗
Keploy has two operating modes:
- Record mode: Keploy injects eBPF hooks to capture incoming HTTP traffic and redirects outgoing TCP/UDP traffic to its proxy server. The proxy server asynchronously captures packets and saves them in YAML files.
- Test mode: Keploy reads YAML files for test cases and stubs/mocks. It starts the application, sends recorded HTTP test cases, and simulates responses for outgoing calls. This ensures no side effects due to non-idempotency.
Installation⌗
Since Keploy uses eBPF under the hood, it can only be used on Linux-based environments, while Windows is not supported and can only run Keploy through WSL. Although macOS has supported eBPF since Big Sur (11.0), the support is limited. Apple implemented a restricted version of BPF on macOS (called Restricted BPF, or rbpf), which is not fully compatible with Linux’s eBPF. Therefore, on macOS, Keploy runs in Docker.
For specific installation methods, please refer to the official documentation.
Experiment⌗
Clone the official example project:
docker network create keploy-network
git clone https://github.com/keploy/samples-go.git && cd samples-go/echo-sql
Cloning into 'samples-go'...
remote: Enumerating objects: 2068, done.
remote: Counting objects: 100% (1540/1540), done.
remote: Compressing objects: 100% (769/769), done.
remote: Total 2068 (delta 838), reused 1210 (delta 664), pack-reused 528 (from 1)
Receiving objects: 100% (2068/2068), 120.09 MiB | 15.36 MiB/s, done.
Resolving deltas: 100% (1004/1004), done.
go mod download
docker build -t echo-app:1.0 .
Modify the docker-compose.yaml
file configuration as follows:
services:
postgres:
image: postgres:10.5
container_name: postgresDb
restart: always
environment:
- POSTGRES_DB=postgres
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=password
ports:
- '5432:5432'
volumes:
# - ./postgres-data:/var/lib/postgresql/data
# copy the sql script to create tables
- ./sql/init.sql:/docker-entrypoint-initdb.d/init.sql
networks:
- keploy-network
networks:
keploy-network:
name: keploy-network
external: true
Start the service:
docker compose up
Run Keploy:
keploy record -c "docker run -p 8082:8082 --name echoSqlApp --network keploy-network echo-app:1.0"
▓██▓▄
▓▓▓▓██▓█▓▄
████████▓▒
▀▓▓███▄ ▄▄ ▄ ▌
▄▌▌▓▓████▄ ██ ▓█▀ ▄▌▀▄ ▓▓▌▄ ▓█ ▄▌▓▓▌▄ ▌▌ ▓
▓█████████▌▓▓ ██▓█▄ ▓█▄▓▓ ▐█▌ ██ ▓█ █▌ ██ █▌ █▓
▓▓▓▓▀▀▀▀▓▓▓▓▓▓▌ ██ █▓ ▓▌▄▄ ▐█▓▄▓█▀ █▓█ ▀█▄▄█▀ █▓█
▓▌ ▐█▌ █▌
▓
version: 2.3.0-beta31
🐰 Keploy: 2024-10-26T08:08:06Z INFO detected that Keploy is running in a docker container
🐰 Keploy: 2024-10-26T08:08:06Z WARN buildDelay is set to 30, incase your docker container takes more time to build use --buildDelay to set custom delay
🐰 Keploy: 2024-10-26T08:08:06Z INFO Example usage: keploy record -c "docker-compose up --build" --buildDelay 35
🐰 Keploy: 2024-10-26T08:08:06Z INFO trying to inject network:keploy-network to the keploy container
🐰 Keploy: 2024-10-26T08:08:06Z INFO Successfully injected network to the keploy container {"Keploy container": "keploy-v2", "appNetwork": "keploy-network", "keploy container ip": "192.168.107.3"}
🐰 Keploy: 2024-10-26T08:08:07Z INFO keploy initialized and probes added to the kernel.
🐰 Keploy: 2024-10-26T08:08:07Z INFO starting UDP DNS server at addr :26789
🐰 Keploy: 2024-10-26T08:08:07Z INFO starting TCP DNS server at addr :26789
🐰 Keploy: 2024-10-26T08:08:07Z INFO Keploy has taken control of the DNS resolution mechanism, your application may misbehave if you have provided wrong domain name in your application code.
🐰 Keploy: 2024-10-26T08:08:07Z INFO Proxy started at port:16789
____ __
/ __/___/ / ___
/ _// __/ _ \/ _ \
/___/\__/_//_/\___/ v4.9.0
High performance, minimalist Go web framework
https://echo.labstack.com
____________________________________O/_______
O\
⇨ http server started on [::]:8082
After running, both Keploy and the service are started. Now you can send requests to the service:
curl --request POST \
--url http://localhost:8082/url \
--header 'content-type: application/json' \
--data '{"url": "https://github.com"}'
{"ts":1729930100440833849,"url":"http://localhost:8082/4KepjkTT"}
curl --request GET \
--url http://localhost:8082/4KepjkTT
When the request is sent, the following logs will be output in the Keploy console:
🐰 Keploy: 2024-10-26T08:08:22Z INFO 🟠 Keploy has captured test cases for the user's application. {"path": "/Users/George/Develop/samples-go/echo-sql/keploy/test-set-1/tests", "testcase name": "test-1"}
🐰 Keploy: 2024-10-26T08:09:03Z INFO 🟠 Keploy has captured test cases for the user's application. {"path": "/Users/George/Develop/samples-go/echo-sql/keploy/test-set-1/tests", "testcase name": "test-3"}
At this point, the requests have been successfully intercepted, and corresponding request record YAML files have been generated. Next, you can check the generated file contents in the project root directory.
First POST request:
version: api.keploy.io/v1beta1
kind: Http
name: test-1
spec:
metadata: {}
req:
method: POST
proto_major: 1
proto_minor: 1
url: http://localhost:8082/url
header:
Accept: '*/*'
Content-Length: "33"
Content-Type: application/json
Host: localhost:8082
User-Agent: curl/8.7.1
body: |-
{
"url": "https://github.com"
}
timestamp: 2024-10-26T08:08:20.436143805Z
resp:
status_code: 200
header:
Content-Length: "66"
Content-Type: application/json; charset=UTF-8
Date: Sat, 26 Oct 2024 08:08:20 GMT
body: |
{"ts":1729930100440833849,"url":"http://localhost:8082/4KepjkTT"}
status_message: OK
proto_major: 0
proto_minor: 0
timestamp: 2024-10-26T08:08:22.510298785Z
objects: []
assertions:
noise:
body.ts: []
header.Date: []
created: 1729930102
curl: |-
curl --request POST \
--url http://localhost:8082/url \
--header 'Host: localhost:8082' \
--header 'User-Agent: curl/8.7.1' \
--header 'Accept: */*' \
--header 'Content-Type: application/json' \
--data "{\n \"url\": \"https://github.com\"\n}"
Second GET request:
version: api.keploy.io/v1beta1
kind: Http
name: test-3
spec:
metadata: {}
req:
method: GET
proto_major: 1
proto_minor: 1
url: http://localhost:8082/4KepjkTT
header:
Accept: '*/*'
Host: localhost:8082
User-Agent: curl/8.7.1
body: ""
timestamp: 2024-10-26T08:09:01.286595576Z
resp:
status_code: 308
header:
Content-Length: "0"
Date: Sat, 26 Oct 2024 08:09:01 GMT
Location: https://github.com
body: ""
status_message: Permanent Redirect
proto_major: 0
proto_minor: 0
timestamp: 2024-10-26T08:09:03.368781888Z
objects: []
assertions:
noise:
header.Date: []
created: 1729930143
curl: |
curl --request GET \
--url http://localhost:8082/4KepjkTT \
--header 'Accept: */*' \
--header 'Host: localhost:8082' \
--header 'User-Agent: curl/8.7.1' \
mocks.yaml file:
version: api.keploy.io/v1beta1
kind: Postgres
name: mock-0
spec:
metadata:
type: config
postgresrequests:
- identifier: StartupRequest
length: 102
payload: AAAAZgADAAB1c2VyAHBvc3RncmVzAGRhdGFiYXNlAHBvc3RncmVzAGRhdGVzdHlsZQBJU08sIE1EWQBjbGllbnRfZW5jb2RpbmcAVVRGOABleHRyYV9mbG9hdF9kaWdpdHMAMgAA
startup_message:
protocolversion: 196608
parameters:
client_encoding: UTF8
database: postgres
datestyle: ISO, MDY
extra_float_digits: "2"
user: postgres
auth_type: 0
postgresresponses:
- header: [R]
identifier: ServerResponse
length: 102
authentication_md5_password:
salt: [145, 212, 36, 22]
msg_type: 82
auth_type: 5
reqtimestampmock: 2024-10-26T08:08:09.723623968Z
restimestampmock: 2024-10-26T08:08:09.725945385Z
connectionId: "0"
---
version: api.keploy.io/v1beta1
kind: Postgres
name: mock-1
spec:
metadata:
type: config
postgresrequests:
- header: [p]
identifier: ClientRequest
length: 102
password_message:
password: md56527563748c9648a3030941e6094c992
msg_type: 112
auth_type: 0
postgresresponses:
- header: [R, S, S, S, S, S, S, S, S, S, S, S, K, Z]
identifier: ServerResponse
length: 102
authentication_md5_password:
salt: [0, 0, 0, 0]
backend_key_data:
process_id: 68
secret_key: 3063486908
parameter_status:
- name: application_name
value: ""
- name: client_encoding
value: UTF8
- name: DateStyle
value: ISO, MDY
- name: integer_datetimes
value: "on"
- name: IntervalStyle
value: postgres
- name: is_superuser
value: "on"
- name: server_encoding
value: UTF8
- name: server_version
value: 10.5 (Debian 10.5-2.pgdg90+1)
- name: session_authorization
value: postgres
- name: standard_conforming_strings
value: "on"
- name: TimeZone
value: UTC
- name: TimeZone
value: UTC
- name: TimeZone
value: UTC
ready_for_query:
txstatus: 73
msg_type: 90
auth_type: 0
reqtimestampmock: 2024-10-26T08:08:09.734120389Z
restimestampmock: 2024-10-26T08:08:09.734230184Z
connectionId: "0"
---
version: api.keploy.io/v1beta1
kind: Postgres
name: mock-2
spec:
metadata:
type: config
postgresrequests:
- header: [Q]
identifier: ClientRequest
length: 102
query:
string: ;
msg_type: 81
auth_type: 0
postgresresponses:
- header: [I, Z]
identifier: ServerResponse
length: 102
authentication_md5_password:
salt: [0, 0, 0, 0]
ready_for_query:
txstatus: 73
msg_type: 90
auth_type: 0
reqtimestampmock: 2024-10-26T08:08:09.734854874Z
restimestampmock: 2024-10-26T08:08:09.734906876Z
connectionId: "0"
---
version: api.keploy.io/v1beta1
kind: Postgres
name: mock-3
spec:
metadata:
type: config
postgresrequests:
- header: [Q]
identifier: ClientRequest
length: 102
payload: UQAAAMsKCQlDUkVBVEUgVEFCTEUgSUYgTk9UIEVYSVNUUyB1cmxfbWFwICgKCQkJaWQgICAgICAgICAgIFZBUkNIQVIoMjU1KSBQUklNQVJZIEtFWSwKCQkJcmVkaXJlY3RfdXJsIFZBUkNIQVIoMjU1KSBOT1QgTlVMTCwKCQkJY3JlYXRlZF9hdCAgIFRJTUVTVEFNUCBOT1QgTlVMTCwKCQkJdXBkYXRlZF9hdCAgIFRJTUVTVEFNUCBOT1QgTlVMTAoJCSk7CgkA
query:
string: ' CREATE TABLE IF NOT EXISTS url_map ( id VARCHAR(255) PRIMARY KEY, redirect_url VARCHAR(255) NOT NULL, created_at TIMESTAMP NOT NULL, updated_at TIMESTAMP NOT NULL ); '
msg_type: 81
auth_type: 0
postgresresponses:
- header: ["N", C, Z]
identifier: ServerResponse
length: 102
payload: TgAAAHRTTk9USUNFAFZOT1RJQ0UAQzQyUDA3AE1yZWxhdGlvbiAidXJsX21hcCIgYWxyZWFkeSBleGlzdHMsIHNraXBwaW5nAEZwYXJzZV91dGlsY21kLmMATDIwOQBSdHJhbnNmb3JtQ3JlYXRlU3RtdAAAQwAAABFDUkVBVEUgVEFCTEUAWgAAAAVJ
authentication_md5_password:
salt: [0, 0, 0, 0]
command_complete:
- command_tag_type: CREATE TABLE
notice_response:
severity: NOTICE
severity_unlocalized: NOTICE
code: 42P07
message: relation "url_map" already exists, skipping
detail: ""
hint: ""
position: 0
internal_position: 0
internal_query: ""
where: ""
schema_name: ""
table_name: ""
column_name: ""
data_type_name: ""
constraint_name: ""
file: parse_utilcmd.c
line: 209
routine: transformCreateStmt
unknown_fields: {}
ready_for_query:
txstatus: 73
msg_type: 90
auth_type: 0
reqtimestampmock: 2024-10-26T08:08:09.737489386Z
restimestampmock: 2024-10-26T08:08:09.737585347Z
connectionId: "0"
---
version: api.keploy.io/v1beta1
kind: Postgres
name: mock-4
spec:
metadata:
type: config
postgresrequests:
- header: [Q]
identifier: ClientRequest
length: 102
query:
string: ;
msg_type: 81
auth_type: 0
postgresresponses:
- header: [I, Z]
identifier: ServerResponse
length: 102
authentication_md5_password:
salt: [0, 0, 0, 0]
ready_for_query:
txstatus: 73
msg_type: 90
auth_type: 0
reqtimestampmock: 2024-10-26T08:08:20.440583674Z
restimestampmock: 2024-10-26T08:08:20.44066376Z
connectionId: "0"
---
version: api.keploy.io/v1beta1
kind: Postgres
name: mock-5
spec:
metadata:
type: config
postgresrequests:
- header: [P, D]
identifier: ClientRequest
length: 102
payload: UAAAADcACgkJCVNFTEVDVCAqCgkJCUZST00gdXJsX21hcAoJCQlXSEVSRSBpZCA9ICQxCgkAAABEAAAABlMAUwAAAAQ=
describe:
object_type: 83
name: ""
parse:
- name: ""
query: ' SELECT * FROM url_map WHERE id = $1 '
parameter_oids: []
msg_type: 68
auth_type: 0
postgresresponses:
- header: ["1", t, T, Z]
identifier: ServerResponse
length: 102
authentication_md5_password:
salt: [0, 0, 0, 0]
parameter_description:
parameteroids:
- 1042
ready_for_query:
txstatus: 73
row_description: {fields: [{field_name: id, table_oid: 16384, table_attribute_number: 1, data_type_oid: 1042, data_type_size: -1, type_modifier: 12, format: 0}, {field_name: redirect_url, table_oid: 16384, table_attribute_number: 2, data_type_oid: 1043, data_type_size: -1, type_modifier: 154, format: 0}, {field_name: created_at, table_oid: 16384, table_attribute_number: 3, data_type_oid: 1114, data_type_size: 8, type_modifier: -1, format: 0}, {field_name: updated_at, table_oid: 16384, table_attribute_number: 4, data_type_oid: 1114, data_type_size: 8, type_modifier: -1, format: 0}]}
msg_type: 90
auth_type: 0
reqtimestampmock: 2024-10-26T08:08:20.441889138Z
restimestampmock: 2024-10-26T08:08:20.442930967Z
connectionId: "0"
---
version: api.keploy.io/v1beta1
kind: Postgres
name: mock-6
spec:
metadata:
type: config
postgresrequests:
- header: [B, E]
identifier: ClientRequest
length: 102
payload: QgAAABgAAAAAAAEAAAAINEtlcGprVFQAAEUAAAAJAAAAAABTAAAABA==
bind:
- parameters: [[52, 75, 101, 112, 106, 107, 84, 84]]
execute:
- {}
msg_type: 69
auth_type: 0
postgresresponses:
- header: ["2", C, Z]
identifier: ServerResponse
length: 102
authentication_md5_password:
salt: [0, 0, 0, 0]
command_complete:
- command_tag_type: SELECT 0
ready_for_query:
txstatus: 73
msg_type: 90
auth_type: 0
reqtimestampmock: 2024-10-26T08:08:20.444335976Z
restimestampmock: 2024-10-26T08:08:20.444560068Z
connectionId: "0"
---
version: api.keploy.io/v1beta1
kind: Postgres
name: mock-7
spec:
metadata:
type: config
postgresrequests:
- header: [P, D]
identifier: ClientRequest
length: 102
payload: UAAAAGUACgkJSU5TRVJUIElOVE8gdXJsX21hcCAoaWQsIHJlZGlyZWN0X3VybCwgY3JlYXRlZF9hdCwgdXBkYXRlZF9hdCkKCQlWQUxVRVMgKCQxLCAkMiwgJDMsICQ0KQoJAAAARAAAAAZTAFMAAAAE
describe:
object_type: 83
name: ""
parse:
- name: ""
query: ' INSERT INTO url_map (id, redirect_url, created_at, updated_at) VALUES ($1, $2, $3, $4) '
parameter_oids: []
msg_type: 68
auth_type: 0
postgresresponses:
- header: ["1", t, "n", Z]
identifier: ServerResponse
length: 102
authentication_md5_password:
salt: [0, 0, 0, 0]
parameter_description:
parameteroids:
- 1042
- 1043
- 1114
- 1114
ready_for_query:
txstatus: 73
msg_type: 90
auth_type: 0
reqtimestampmock: 2024-10-26T08:08:20.445084295Z
restimestampmock: 2024-10-26T08:08:20.445454558Z
connectionId: "0"
---
version: api.keploy.io/v1beta1
kind: Postgres
name: mock-8
spec:
metadata:
type: config
postgresrequests:
- header: [B, E]
identifier: ClientRequest
length: 102
payload: QgAAAHIAAAAAAAQAAAAINEtlcGprVFQAAAASaHR0cHM6Ly9naXRodWIuY29tAAAAHjIwMjQtMTAtMjYgMDg6MDg6MjAuNDQwODMzODQ5WgAAAB4yMDI0LTEwLTI2IDA4OjA4OjIwLjQ0MDgzMzg0OVoAAEUAAAAJAAAAAABTAAAABA==
bind:
- parameters: [[52, 75, 101, 112, 106, 107, 84, 84], [104, 116, 116, 112, 115, 58, 47, 47, 103, 105, 116, 104, 117, 98, 46, 99, 111, 109], [50, 48, 50, 52, 45, 49, 48, 45, 50, 54, 32, 48, 56, 58, 48, 56, 58, 50, 48, 46, 52, 52, 48, 56, 51, 51, 56, 52, 57, 90], [50, 48, 50, 52, 45, 49, 48, 45, 50, 54, 32, 48, 56, 58, 48, 56, 58, 50, 48, 46, 52, 52, 48, 56, 51, 51, 56, 52, 57, 90]]
execute:
- {}
msg_type: 69
auth_type: 0
postgresresponses:
- header: ["2", C, Z]
identifier: ServerResponse
length: 102
authentication_md5_password:
salt: [0, 0, 0, 0]
command_complete:
- command_tag_type: INSERT 0 1
ready_for_query:
txstatus: 73
msg_type: 90
auth_type: 0
reqtimestampmock: 2024-10-26T08:08:20.451974127Z
restimestampmock: 2024-10-26T08:08:20.451997753Z
connectionId: "0"
---
version: api.keploy.io/v1beta1
kind: Postgres
name: mock-9
spec:
metadata:
type: config
postgresrequests:
- header: [Q]
identifier: ClientRequest
length: 102
query:
string: ;
msg_type: 81
auth_type: 0
postgresresponses:
- header: [I, Z]
identifier: ServerResponse
length: 102
authentication_md5_password:
salt: [0, 0, 0, 0]
ready_for_query:
txstatus: 73
msg_type: 90
auth_type: 0
reqtimestampmock: 2024-10-26T08:08:45.584922216Z
restimestampmock: 2024-10-26T08:08:45.584979593Z
connectionId: "0"
---
version: api.keploy.io/v1beta1
kind: Postgres
name: mock-10
spec:
metadata:
type: config
postgresrequests:
- header: [P, D]
identifier: ClientRequest
length: 102
payload: UAAAADQACgkJU0VMRUNUICoKCQlGUk9NIHVybF9tYXAKCQlXSEVSRSBpZCA9ICQxCgkAAABEAAAABlMAUwAAAAQ=
describe:
object_type: 83
name: ""
parse:
- name: ""
query: ' SELECT * FROM url_map WHERE id = $1 '
parameter_oids: []
msg_type: 68
auth_type: 0
postgresresponses:
- header: ["1", t, T, Z]
identifier: ServerResponse
length: 102
authentication_md5_password:
salt: [0, 0, 0, 0]
parameter_description:
parameteroids:
- 1042
ready_for_query:
txstatus: 73
row_description: {fields: [{field_name: id, table_oid: 16384, table_attribute_number: 1, data_type_oid: 1042, data_type_size: -1, type_modifier: 12, format: 0}, {field_name: redirect_url, table_oid: 16384, table_attribute_number: 2, data_type_oid: 1043, data_type_size: -1, type_modifier: 154, format: 0}, {field_name: created_at, table_oid: 16384, table_attribute_number: 3, data_type_oid: 1114, data_type_size: 8, type_modifier: -1, format: 0}, {field_name: updated_at, table_oid: 16384, table_attribute_number: 4, data_type_oid: 1114, data_type_size: 8, type_modifier: -1, format: 0}]}
msg_type: 90
auth_type: 0
reqtimestampmock: 2024-10-26T08:08:45.585466111Z
restimestampmock: 2024-10-26T08:08:45.585555489Z
connectionId: "0"
---
version: api.keploy.io/v1beta1
kind: Postgres
name: mock-11
spec:
metadata:
type: config
postgresrequests:
- header: [B, E]
identifier: ClientRequest
length: 102
payload: QgAAABgAAAAAAAEAAAAIR3V3SENnb1EAAEUAAAAJAAAAAABTAAAABA==
bind:
- parameters: [[71, 117, 119, 72, 67, 103, 111, 81]]
execute:
- {}
msg_type: 69
auth_type: 0
postgresresponses:
- header: ["2", C, Z]
identifier: ServerResponse
length: 102
authentication_md5_password:
salt: [0, 0, 0, 0]
command_complete:
- command_tag_type: SELECT 0
ready_for_query:
txstatus: 73
msg_type: 90
auth_type: 0
reqtimestampmock: 2024-10-26T08:08:45.588724562Z
restimestampmock: 2024-10-26T08:08:45.588848191Z
connectionId: "0"
---
version: api.keploy.io/v1beta1
kind: Postgres
name: mock-12
spec:
metadata:
type: config
postgresrequests:
- header: [Q]
identifier: ClientRequest
length: 102
query:
string: ;
msg_type: 81
auth_type: 0
postgresresponses:
- header: [I, Z]
identifier: ServerResponse
length: 102
authentication_md5_password:
salt: [0, 0, 0, 0]
ready_for_query:
txstatus: 73
msg_type: 90
auth_type: 0
reqtimestampmock: 2024-10-26T08:09:01.287327019Z
restimestampmock: 2024-10-26T08:09:01.287367771Z
connectionId: "0"
---
version: api.keploy.io/v1beta1
kind: Postgres
name: mock-13
spec:
metadata:
type: config
postgresrequests:
- header: [P, D]
identifier: ClientRequest
length: 102
payload: UAAAADQACgkJU0VMRUNUICoKCQlGUk9NIHVybF9tYXAKCQlXSEVSRSBpZCA9ICQxCgkAAABEAAAABlMAUwAAAAQ=
describe:
object_type: 83
name: ""
parse:
- name: ""
query: ' SELECT * FROM url_map WHERE id = $1 '
parameter_oids: []
msg_type: 68
auth_type: 0
postgresresponses:
- header: ["1", t, T, Z]
identifier: ServerResponse
length: 102
authentication_md5_password:
salt: [0, 0, 0, 0]
parameter_description:
parameteroids:
- 1042
ready_for_query:
txstatus: 73
row_description: {fields: [{field_name: id, table_oid: 16384, table_attribute_number: 1, data_type_oid: 1042, data_type_size: -1, type_modifier: 12, format: 0}, {field_name: redirect_url, table_oid: 16384, table_attribute_number: 2, data_type_oid: 1043, data_type_size: -1, type_modifier: 154, format: 0}, {field_name: created_at, table_oid: 16384, table_attribute_number: 3, data_type_oid: 1114, data_type_size: 8, type_modifier: -1, format: 0}, {field_name: updated_at, table_oid: 16384, table_attribute_number: 4, data_type_oid: 1114, data_type_size: 8, type_modifier: -1, format: 0}]}
msg_type: 90
auth_type: 0
reqtimestampmock: 2024-10-26T08:09:01.287893998Z
restimestampmock: 2024-10-26T08:09:01.287930416Z
connectionId: "0"
You can see that the mocks.yaml
file also records the SQL executed in PostgreSQL, which is what was mentioned above about ensuring no side effects due to non-idempotency in test mode.
CI/CD Integration⌗
.gitlab-ci.yml
:
---
stages:
- test
keploy-test-job: # This job runs in the test stage.
image: ubuntu:latest
stage: test
before_script:
## Add the dependencies && Install Keploy Binary
- apt update && apt install -y sudo curl
- curl --silent --location "https://github.com/keploy/keploy/releases/latest/download/keploy_linux_amd64.tar.gz" | tar xz --overwrite -C /tmp
- sudo mkdir -p /usr/local/bin && sudo mv /tmp/keploy /usr/local/bin/keploy
- sudo mount -t debugfs debugfs /sys/kernel/debug
script:
## Steps to run application
...
In the testing phase of CI/CD, we can use the previously recorded request records and mocks configurations to replay requests!
Conclusion⌗
Currently, some API Docs management tools I know, such as Postman and Apifox, have a certain cost to integrate into existing CI/CD Pipelines. They cannot achieve true self-hosting, while Keploy can achieve true self-hosting and minimize test side effects to the greatest extent!
The power of this tool lies in its ability to analyze the dependencies of backend services, such as API requests to upstream services, without requiring code or complex configurations. This is something that cannot be achieved in traditional development experiences and can only be done manually by developers!
I hope this is helpful, Happy hacking…