No results for

Powered byAlgolia

API CRUD Operations

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:

  1. Create a new resource, a "croc".
  2. Read the list of "crocs".
  3. Update the name of the "croc" and read the "croc" to confirm the update operation.
  4. 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: 1
7};
8
9// Create a random string of given length
10function 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 requests
23export 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 requests
46 const requestConfigWithTag = (tag) => ({
47 headers: {
48 Authorization: `Bearer ${authToken}`,
49 },
50 tags: Object.assign(
51 {},
52 {
53 name: 'PrivateCrocs',
54 },
55 tag
56 ),
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 iteration
7 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 requests
17export 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: PASSWORD
37 });
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 requests
51 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 worked
91 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}