@@ -3,7 +3,7 @@ title: 'API CRUD Operations'
3
3
excerpt : ' This example covers the usage of k6 to test a REST API CRUD operations.'
4
4
---
5
5
6
- The example showcases the testing of CRUD operations on a REST API.
6
+ The examples showcase the testing of CRUD operations on a REST API.
7
7
8
8
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:
9
9
@@ -12,6 +12,8 @@ CRUD refers to the basic operations in a database: Create, Read, Update, and Del
12
12
- _ Update_ : HTTP ` PUT ` or ` PATCH ` to change an existing resource.
13
13
- _ Delete_ : HTTP ` DELETE ` to remove a resource.
14
14
15
+ You'll find two test examples: the first one uses the core k6 APIs (` k6/http ` and ` checks ` ), and the second showcases more recent APIs (` httpx ` and ` k6chaijs ` ).
16
+
15
17
## Test steps
16
18
17
19
In the [ setup() stage] ( /using-k6/test-lifecycle/#setup-and-teardown-stages ) we create a user for the [ k6 HTTP REST API] ( https://test-api.k6.io/ ) . We then retrieve and return a bearer token to authenticate the next CRUD requests.
@@ -23,12 +25,11 @@ The steps implemented in the [VU stage](/using-k6/test-lifecycle/#the-vu-stage)
23
25
3 . _ Update_ the name of the "croc" and _ read_ the "croc" to confirm the update operation.
24
26
4 . _ Delete_ the "croc" resource.
25
27
26
- <CodeGroup labels={[ "api-crud-operations.js"] } lineNumbers={[ true] }>
28
+ <CodeGroup labels={[ "api-crud-operations-k6-core-apis .js"] } lineNumbers={[ true] }>
27
29
28
30
``` javascript
29
- import { describe , expect } from ' https://jslib.k6.io/k6chaijs/4.3.4.3/index.js' ;
30
- import { Httpx } from ' https://jslib.k6.io/httpx/0.1.0/index.js' ;
31
- import { randomIntBetween , randomItem } from " https://jslib.k6.io/k6-utils/1.2.0/index.js" ;
31
+ import http from ' k6/http' ;
32
+ import { check , group , fail } from ' k6' ;
32
33
33
34
export const options = {
34
35
thresholds: {
@@ -43,11 +44,6 @@ export const options = {
43
44
iterations: 1
44
45
};
45
46
46
- const USERNAME = ` user${ randomIntBetween (1 , 100000 )} @example.com` ; // Set your own email;
47
- const PASSWORD = ' superCroc2019' ;
48
-
49
- const session = new Httpx ({ baseURL: ' https://test-api.k6.io' });
50
-
51
47
// Create a random string of given length
52
48
function randomString (length , charset = ' ' ) {
53
49
if (! charset) charset = ' abcdefghijklmnopqrstuvwxyz' ;
@@ -56,7 +52,131 @@ function randomString(length, charset = '') {
56
52
return res;
57
53
}
58
54
59
- // Authenticate user and retrieve authentication token for the API requests
55
+ const USERNAME = ` ${ randomString (10 )} @example.com` ; // Set your own email or `${randomString(10)}@example.com`;
56
+ const PASSWORD = ' superCroc2019' ;
57
+
58
+ const BASE_URL = ' https://test-api.k6.io' ;
59
+
60
+ // Register a new user and retrieve authentication token for subsequent API requests
61
+ export function setup () {
62
+ const res = http .post (` ${ BASE_URL } /user/register/` , {
63
+ first_name: ' Crocodile' ,
64
+ last_name: ' Owner' ,
65
+ username: USERNAME ,
66
+ password: PASSWORD ,
67
+ });
68
+
69
+ check (res, { ' created user ' : (r ) => r .status === 201 });
70
+
71
+ const loginRes = http .post (` ${ BASE_URL } /auth/token/login/` , {
72
+ username: USERNAME ,
73
+ password: PASSWORD ,
74
+ });
75
+
76
+ const authToken = loginRes .json (' access' );
77
+ check (authToken, { ' logged in successfully ' : () => authToken !== ' ' });
78
+
79
+ return authToken;
80
+ }
81
+
82
+ export default (authToken ) => {
83
+ // set the authorization header on the session for the subsequent requests
84
+ const requestConfigWithTag = (tag ) => ({
85
+ headers: {
86
+ Authorization: ` Bearer ${ authToken} ` ,
87
+ },
88
+ tags: Object .assign (
89
+ {},
90
+ {
91
+ name: ' PrivateCrocs' ,
92
+ },
93
+ tag
94
+ ),
95
+ });
96
+
97
+ let URL = ` ${ BASE_URL } /my/crocodiles/` ;
98
+
99
+ group (' 01. Create a new crocodile' , () => {
100
+ const payload = {
101
+ name: ` Name ${ randomString (10 )} ` ,
102
+ sex: ' F' ,
103
+ date_of_birth: ' 2023-05-11' ,
104
+ };
105
+
106
+ const res = http .post (URL , payload, requestConfigWithTag ({ name: ' Create' }));
107
+
108
+ if (check (res, { ' Croc created correctly ' : (r ) => r .status === 201 })) {
109
+ URL = ` ${ URL }${ res .json (' id' )} /` ;
110
+ } else {
111
+ console .log (` Unable to create a Croc ${ res .status } ${ res .body } ` );
112
+ return ;
113
+ }
114
+ });
115
+
116
+ group (' 02. Fetch private crocs' , () => {
117
+ const res = http .get (` ${ BASE_URL } /my/crocodiles/` , requestConfigWithTag ({ name: ' Fetch' }));
118
+ check (res, { ' retrieved crocs status ' : (r ) => r .status === 200 });
119
+ check (res .json (), { ' retrieved crocs list ' : (r ) => r .length > 0 });
120
+ });
121
+
122
+ group (' 03. Update the croc' , () => {
123
+ const payload = { name: ' New name' };
124
+ const res = http .patch (URL , payload, requestConfigWithTag ({ name: ' Update' }));
125
+ const isSuccessfulUpdate = check (res, {
126
+ ' Update worked ' : () => res .status === 200 ,
127
+ ' Updated name is correct ' : () => res .json (' name' ) === ' New name' ,
128
+ });
129
+
130
+ if (! isSuccessfulUpdate) {
131
+ console .log (` Unable to update the croc ${ res .status } ${ res .body } ` );
132
+ return ;
133
+ }
134
+ });
135
+
136
+ group (' 04. Delete the croc' , () => {
137
+ const delRes = http .del (URL , null , requestConfigWithTag ({ name: ' Delete' }));
138
+
139
+ const isSuccessfulDelete = check (null , {
140
+ ' Croc was deleted correctly ' : () => delRes .status === 204 ,
141
+ });
142
+
143
+ if (! isSuccessfulDelete) {
144
+ console .log (` Croc was not deleted properly` );
145
+ return ;
146
+ }
147
+ });
148
+
149
+ };
150
+ ```
151
+
152
+ </CodeGroup >
153
+
154
+ <CodeGroup labels={[ "api-crud-operations-k6-new-apis.js"] } lineNumbers={[ true] }>
155
+
156
+ ``` javascript
157
+ import { describe , expect } from ' https://jslib.k6.io/k6chaijs/4.3.4.3/index.js' ;
158
+ import { Httpx } from ' https://jslib.k6.io/httpx/0.1.0/index.js' ;
159
+ import { randomIntBetween , randomItem , randomString } from " https://jslib.k6.io/k6-utils/1.2.0/index.js" ;
160
+
161
+ export const options = {
162
+ thresholds: {
163
+ checks: [{
164
+ threshold: ' rate == 1.00' , abortOnFail: true ,
165
+ }],
166
+ ' http_req_duration' : [' p(90)<25000' , ' p(95)<30000' ],
167
+ ' http_req_duration{name:Create}' : [' avg<15000' , ' max<25000' ],
168
+ },
169
+ // for the example, let's run only 1 VU with 1 iteration
170
+ vus: 1 ,
171
+ iterations: 1 ,
172
+ };
173
+
174
+ const USERNAME = ` user${ randomIntBetween (1 , 100000 )} @example.com` ; // Set your own email;
175
+ const PASSWORD = ' superCroc2019' ;
176
+
177
+ const session = new Httpx ({ baseURL: ' https://test-api.k6.io' });
178
+
179
+ // Register a new user and retrieve authentication token for subsequent API requests
60
180
export function setup () {
61
181
62
182
let authToken = null ;
@@ -70,7 +190,7 @@ export function setup() {
70
190
});
71
191
72
192
expect (resp .status , ' User create status' ).to .equal (201 );
73
- expect (resp).to .have .validJsonBody ();
193
+ expect (resp, ' User create valid json response ' ).to .have .validJsonBody ();
74
194
});
75
195
76
196
describe (` setup - Authenticate the new user ${ USERNAME } ` , () => {
@@ -79,18 +199,18 @@ export function setup() {
79
199
password: PASSWORD
80
200
});
81
201
82
- expect (resp .status , ' Auth status' ).to .equal (200 );
83
- expect (resp).to .have .validJsonBody ();
202
+ expect (resp .status , ' Authenticate status' ).to .equal (200 );
203
+ expect (resp, ' Authenticate valid json response ' ).to .have .validJsonBody ();
84
204
authToken = resp .json (' access' );
85
- expect (authToken, ' auth token' ).to .not .be .null ;
205
+ expect (authToken, ' Authentication token ' , ' auth token' ).to .not .be .null ;
86
206
});
87
207
88
208
return authToken;
89
209
}
90
210
91
211
export default function (authToken ) {
92
212
93
- // set the authorization header on the session for the subsequent requests.
213
+ // set the authorization header on the session for the subsequent requests
94
214
session .addHeader (' Authorization' , ` Bearer ${ authToken} ` );
95
215
96
216
describe (' 01. Create a new crocodile' , (t ) => {
@@ -104,7 +224,7 @@ export default function (authToken) {
104
224
const resp = session .post (` /my/crocodiles/` , payload);
105
225
106
226
expect (resp .status , ' Croc creation status' ).to .equal (201 );
107
- expect (resp).to .have .validJsonBody ();
227
+ expect (resp, ' Croc creation valid json response ' ).to .have .validJsonBody ();
108
228
109
229
session .newCrocId = resp .json (' id' );
110
230
})
@@ -115,8 +235,8 @@ export default function (authToken) {
115
235
const resp = session .get (' /my/crocodiles/' );
116
236
117
237
expect (resp .status , ' Fetch croc status' ).to .equal (200 );
118
- expect (resp).to .have .validJsonBody ();
119
- expect (resp .json ().length , ' number of crocs' ).to .be .above (0 );
238
+ expect (resp, ' Fetch croc valid json response ' ).to .have .validJsonBody ();
239
+ expect (resp .json ().length , ' Number of crocs' ).to .be .above (0 );
120
240
})
121
241
122
242
describe (' 03. Update the croc' , (t ) => {
@@ -127,15 +247,15 @@ export default function (authToken) {
127
247
const resp = session .patch (` /my/crocodiles/${ session .newCrocId } /` , payload);
128
248
129
249
expect (resp .status , ' Croc patch status' ).to .equal (200 );
130
- expect (resp).to .have .validJsonBody ();
131
- expect (resp .json (' name' )).to .equal (payload .name );
250
+ expect (resp, ' Fetch croc valid json response ' ).to .have .validJsonBody ();
251
+ expect (resp .json (' name' ), ' Croc name ' ).to .equal (payload .name );
132
252
133
253
// read "croc" again to verify the update worked
134
254
const resp1 = session .get (` /my/crocodiles/${ session .newCrocId } /` );
135
255
136
256
expect (resp1 .status , ' Croc fetch status' ).to .equal (200 );
137
- expect (resp1).to .have .validJsonBody ();
138
- expect (resp1 .json (' name' )).to .equal (payload .name );
257
+ expect (resp1, ' Fetch croc valid json response ' ).to .have .validJsonBody ();
258
+ expect (resp1 .json (' name' ), ' Croc name ' ).to .equal (payload .name );
139
259
140
260
})
141
261
0 commit comments