The examples showcase the testing of CRUD operations on a REST API.
CRUD refers to the basic operations in a database: Create, Read, Update, and Delete. We can map these operations to HTTP methods in REST APIs:
- Create: HTTP POST operation to create a new resource.
- Read: HTTP GET to retrieve a resource.
- Update: HTTP PUTor PATCH to change an existing resource.
- Delete: HTTP DELETE to remove a resource.
This document has two examples, one that uses the core k6 APIs (k6/http and checks) and another to show the more recent APIs (httpx and k6chaijs).
Test steps
In the setup() stage we create a user for the k6 HTTP REST API. We then retrieve and return a bearer token to authenticate the next CRUD requests.
The steps implemented in the VU stage are as follows:
- Create a new resource, a "croc".
- Read the list of "crocs".
- Update the name of the "croc" and read the "croc" to confirm the update operation.
- Delete the "croc" resource.
Core k6 APIs example
api-crud-operations-k6-core-apis.js
1import http from 'k6/http';2import { check, group, fail } from 'k6';3
4export const options = {5 vus: 1,6 iterations: 17};8
9// Create a random string of given length10function randomString(length, charset = '') {11 if (!charset) charset = 'abcdefghijklmnopqrstuvwxyz';12 let res = '';13 while (length--) res += charset[(Math.random() * charset.length) | 0];14 return res;15}16
17const USERNAME = `${randomString(10)}@example.com`; // Set your own email or `${randomString(10)}@example.com`;18const PASSWORD = 'superCroc2019';19
20const BASE_URL = 'https://test-api.k6.io';21
22// Register a new user and retrieve authentication token for subsequent API requests23export function setup() {24 const res = http.post(`${BASE_URL}/user/register/`, {25 first_name: 'Crocodile',26 last_name: 'Owner',27 username: USERNAME,28 password: PASSWORD,29 });30
31 check(res, { 'created user': (r) => r.status === 201 });32
33 const loginRes = http.post(`${BASE_URL}/auth/token/login/`, {34 username: USERNAME,35 password: PASSWORD,36 });37
38 const authToken = loginRes.json('access');39 check(authToken, { 'logged in successfully': () => authToken !== '' });40
41 return authToken;42}43
44export default (authToken) => {45 // set the authorization header on the session for the subsequent requests46 const requestConfigWithTag = (tag) => ({47 headers: {48 Authorization: `Bearer ${authToken}`,49 },50 tags: Object.assign(51 {},52 {53 name: 'PrivateCrocs',54 },55 tag56 ),57 });58
59 let URL = `${BASE_URL}/my/crocodiles/`;60
61 group('01. Create a new crocodile', () => {62 const payload = {63 name: `Name ${randomString(10)}`,64 sex: 'F',65 date_of_birth: '2023-05-11',66 };67
68 const res = http.post(URL, payload, requestConfigWithTag({ name: 'Create' }));69
70 if (check(res, { 'Croc created correctly': (r) => r.status === 201 })) {71 URL = `${URL}${res.json('id')}/`;72 } else {73 console.log(`Unable to create a Croc ${res.status} ${res.body}`);74 return;75 }76 });77
78 group('02. Fetch private crocs', () => {79 const res = http.get(`${BASE_URL}/my/crocodiles/`, requestConfigWithTag({ name: 'Fetch' }));80 check(res, { 'retrieved crocs status': (r) => r.status === 200 });81 check(res.json(), { 'retrieved crocs list': (r) => r.length > 0 });82 });83
84 group('03. Update the croc', () => {85 const payload = { name: 'New name' };86 const res = http.patch(URL, payload, requestConfigWithTag({ name: 'Update' }));87 const isSuccessfulUpdate = check(res, {88 'Update worked': () => res.status === 200,89 'Updated name is correct': () => res.json('name') === 'New name',90 });91
92 if (!isSuccessfulUpdate) {93 console.log(`Unable to update the croc ${res.status} ${res.body}`);94 return;95 }96 });97
98 group('04. Delete the croc', () => {99 const delRes = http.del(URL, null, requestConfigWithTag({ name: 'Delete' }));100
101 const isSuccessfulDelete = check(null, {102 'Croc was deleted correctly': () => delRes.status === 204,103 });104
105 if (!isSuccessfulDelete) {106 console.log(`Croc was not deleted properly`);107 return;108 }109 });110
111};
httpx and k6chaijs example
api-crud-operations-k6-new-apis.js
1import { describe, expect } from 'https://jslib.k6.io/k6chaijs/4.3.4.3/index.js';2import { Httpx } from 'https://jslib.k6.io/httpx/0.1.0/index.js';3import { randomIntBetween, randomItem, randomString } from "https://jslib.k6.io/k6-utils/1.2.0/index.js";4
5export const options = {6 // for the example, let's run only 1 VU with 1 iteration7 vus: 1,8 iterations: 1,9};10
11const USERNAME = `user${randomIntBetween(1, 100000)}@example.com`; // Set your own email;12const PASSWORD = 'superCroc2019';13
14const session = new Httpx({ baseURL: 'https://test-api.k6.io' });15
16// Register a new user and retrieve authentication token for subsequent API requests17export function setup() {18
19 let authToken = null;20
21 describe(`setup - create a test user ${USERNAME}`, () => {22 const resp = session.post(`/user/register/`, {23 first_name: 'Crocodile',24 last_name: 'Owner',25 username: USERNAME,26 password: PASSWORD,27 });28
29 expect(resp.status, 'User create status').to.equal(201);30 expect(resp, 'User create valid json response').to.have.validJsonBody();31 });32
33 describe(`setup - Authenticate the new user ${USERNAME}`, () => {34 const resp = session.post(`/auth/token/login/`, {35 username: USERNAME,36 password: PASSWORD37 });38
39 expect(resp.status, 'Authenticate status').to.equal(200);40 expect(resp, 'Authenticate valid json response').to.have.validJsonBody();41 authToken = resp.json('access');42 expect(authToken, 'Authentication token').to.be.a('string');43 });44
45 return authToken;46}47
48export default function (authToken) {49
50 // set the authorization header on the session for the subsequent requests51 session.addHeader('Authorization', `Bearer ${authToken}`);52
53 describe('01. Create a new crocodile', (t) => {54 const payload = {55 name: `Croc name ${randomString(10)}`,56 sex: randomItem(["M", "F"]),57 date_of_birth: '2023-05-11',58 };59
60 session.addTag('name', 'Create');61 const resp = session.post(`/my/crocodiles/`, payload);62
63 expect(resp.status, 'Croc creation status').to.equal(201);64 expect(resp, 'Croc creation valid json response').to.have.validJsonBody();65
66 session.newCrocId = resp.json('id');67 })68
69 describe('02. Fetch private crocs', (t) => {70
71 session.clearTag('name');72 const resp = session.get('/my/crocodiles/');73
74 expect(resp.status, 'Fetch croc status').to.equal(200);75 expect(resp, 'Fetch croc valid json response').to.have.validJsonBody();76 expect(resp.json().length, 'Number of crocs').to.be.above(0);77 })78
79 describe('03. Update the croc', (t) => {80 const payload = {81 name: `New croc name ${randomString(10)}`,82 };83
84 const resp = session.patch(`/my/crocodiles/${session.newCrocId}/`, payload);85
86 expect(resp.status, 'Croc patch status').to.equal(200);87 expect(resp, 'Fetch croc valid json response').to.have.validJsonBody();88 expect(resp.json('name'), 'Croc name').to.equal(payload.name);89
90 // read "croc" again to verify the update worked91 const resp1 = session.get(`/my/crocodiles/${session.newCrocId}/`);92
93 expect(resp1.status, 'Croc fetch status').to.equal(200);94 expect(resp1, 'Fetch croc valid json response').to.have.validJsonBody();95 expect(resp1.json('name'), 'Croc name').to.equal(payload.name);96
97 })98
99 describe('04. Delete the croc', (t) => {100
101 const resp = session.delete(`/my/crocodiles/${session.newCrocId}/`);102
103 expect(resp.status, 'Croc delete status').to.equal(204);104 });105}