UNPKG

8.01 kBMarkdownView Raw
1# flowbench
2
3[![Build Status](https://travis-ci.org/pgte/flowbench.svg?branch=master)](https://travis-ci.org/pgte/flowbench)
4
5HTTP traffic generator. Supports user flows with alternative paths.
6Stores stats on latency.
7
8# Install
9
10```
11$ npm install flowbench
12```
13
14
15# Use
16
17```js
18var flowbench = require('flowbench');
19
20var experiment = flowbench({
21 base: 'http://localhost:3000',
22 population: 100,
23 maxConcurrentFlows: 50,
24 requestDefaults: {
25 timeout: 10000,
26 jar: false
27 }
28});
29
30experiment
31 .flow({probability: 0.6})
32 .get('/', {id: 1})
33 .verify(verifyResponse1Function)
34 .wait(500)
35 .post('/abc', {
36 id: 2,
37 body: {a: "static value", b: "<%=fixtures.b.random()%>"},
38 fixtures: {
39 b: ['VALUE1', 'VALUE2', 'VALUE3']},
40 timeout: 4000
41 })
42 .verify(
43 flowbench.verify.response.status(200),
44 flowbench.verify.response.body({a: '#{req.body.b}'}))
45 .flow({probability: 0.5})
46 .post('/abc/<%= res[2].prop2 %>',
47 {body: {a: "<%= res[1].prop1 %>", "b": "<%= res[2].prop2} %>"}})
48 .verify(...)
49 .end()
50 .flow({probability: 0.5})
51 .get('/abc')
52 .verify(...)
53 .end()
54 .end()
55 .flow({probability: 0.4})
56 .get('/')
57 .verify(verifyResponse1Function);
58
59
60experiment.begin(function(err, stats) {
61 if (err) {
62 throw err;
63 }
64 console.log('finished. stats:', JSON.stringify(stats, null, ' '));
65});
66```
67
68# API
69
70## flowbench(options)
71
72Options defaults:
73
74```js
75{
76 population: 1,
77 maxConcurrentFlows: Infinity,
78 requestDefaults: {
79 pool: {
80 maxSockets: Infinity
81 },
82 timeout: 10e3
83 }
84};
85```
86
87the `requestDefaults` object is the options for creating a [scoped request](https://github.com/request/request#requestdefaultsoptions).
88
89Returns an Experiment
90
91## Experiment
92
93### experient.flow(options)
94
95Adds an alternative flow to the experiment.
96
97Options:
98
99* `probability` - when more than one sibiling flow is present, this represents the probability of this flow getting executed.
100
101All flows within an experiment are alternative, and are given equal probability (unless otherwise specified.)
102
103Returns an instance of a Flow.
104
105### experiment.begin(cb)
106
107Begins an experiment. Callsback when there is an error or the experiment finishes.
108
109The callback has the following signature:
110
111```
112function callback(err, stats) {}
113```
114
115The `stats` object is something like this:
116
117```js
118{
119 "requestsPerSecond": {
120 "mean": 1651.547543071806,
121 "count": 2000,
122 "currentRate": 1651.4908801787194,
123 "1MinuteRate": 0,
124 "5MinuteRate": 0,
125 "15MinuteRate": 0
126 },
127 "latencyNs": {
128 "min": 397537333,
129 "max": 489818898,
130 "sum": 881597582934,
131 "variance": 493325414798874.75,
132 "mean": 440798791.467,
133 "stddev": 22210930.07505257,
134 "count": 2000,
135 "median": 446440646.5,
136 "p75": 454043121.5,
137 "p95": 478719555.34999996,
138 "p99": 488775828.4,
139 "p999": 489641718.259
140 },
141 "requests": {
142 "GET http://localhost:9000/abc": {
143 "latencyNs": {
144 "min": 429215073,
145 "max": 489818898,
146 "sum": 454618892085,
147 "variance": 201579551941901.38,
148 "mean": 454618892.085,
149 "stddev": 14197871.387708137,
150 "count": 1000,
151 "median": 449254332.5,
152 "p75": 463742870,
153 "p95": 486903385.4,
154 "p99": 488928787.48,
155 "p999": 489818732.511
156 },
157 "statusCodes": {
158 "200": {
159 "count": 1000,
160 "percentage": 1
161 }
162 }
163 },
164 "POST http://localhost:9000/def": {
165 "latencyNs": {
166 "min": 397537333,
167 "max": 459961256,
168 "sum": 426978690849,
169 "variance": 403192361971691.8,
170 "mean": 426978690.849,
171 "stddev": 20079650.44445973,
172 "count": 1000,
173 "median": 419389668,
174 "p75": 445073831.5,
175 "p95": 459471652.6,
176 "p99": 459851196.18,
177 "p999": 459961244.691
178 },
179 "statusCodes": {
180 "201": {
181 "count": 1000,
182 "percentage": 1
183 }
184 }
185 }
186 },
187 "statusCodes": {
188 "200": {
189 "count": 1000,
190 "percentage": 0.5
191 },
192 "201": {
193 "count": 1000,
194 "percentage": 0.5
195 }
196 }
197}
198```
199
200### Emitted events
201
202An Experience instance emits the following events:
203
204* `error (error)` — when an unrecoverrable error occurs.
205* `request (request)` - when a request is made.
206* `end ()` — once the experiment ends.
207* `request-error (req, err)` — when a request errors.
208* `verify-error (err, req, res)` — when a verification error occurs.
209
210
211## Flow
212
213One flow executes the requests added to it in sequence. You can add subflows to a flow (only after the requests have been specified).
214
215### flow.flow(options)
216
217Creates a child flow.
218
219Options:
220
221* `probability` - when more than one sibiling flow is present, this represents the probability of this flow getting executed.
222
223Returns a flow.
224
225
226### flow.end()
227
228Returns the parent flow (or experiment, if at root).
229
230### flow.request(method, url[, options])
231
232Add a request to a flow.
233
234Options:
235
236* id: a string identifying the request. Can access it later inside templates.
237* fixtures: See the [Fixtures](#fixtures) section below.
238* body: object or string, representing the request body
239* headers: object with headers
240* qs: an object with the query string names and values
241* form: sets the body to a querystring representation
242* jar: cookie jar or `false`
243* ... all other options supported by [request](https://github.com/request/request).
244
245### flow.get(url[, options]), flow.post, flow.put, flow.delete, flow.head
246
247Helpers for `flow.request()`.
248
249
250### flow.verify(fn)
251
252Pass in a verification function. This function has the following signature:
253
254```js
255function(req, res) {}
256```
257
258This function will then be responsible for verifying the latest request and response.
259
260If the verification fails, this function can either:
261
262* return an `Error` object
263* return `false`
264* throw an `Error` object
265
266Otherwise, if verification passed, this function should return `true`.
267
268### flow builtin verifiers
269
270You can use the following verifiers:
271
272#### flowbench.verify.response.status
273
274Example:
275
276```
277flow.verify(flowbench.verify.response.status(201));
278```
279
280#### flowbench.verify.response.body
281
282Example:
283
284```js
285flow.verify(flowbench.verify.response.body({a:1, b:2}));
286```
287
288
289## About string interpolation and templating
290
291In option you pass into the request (url, options), you can use strings as EJS templates. These templates can access these objects:
292
293* req: an object with all requests performed, addressed by `id`.
294* res: an object with all the responses received, addressed by `id`.
295
296(see first example above of using ids and templates).
297
298
299### Functions instead of values
300
301In any of the `url` or `options` for a request, you can pass in a function with the followig signature to be evaluated at run time:
302
303```js
304function (req, res, fixtures) {}
305```
306
307## Fixtures
308
309You can define fixtures for any given request, and you can use these fixtures in your request options.
310
311For instance, you can have a given set of airports as fixtures that you can use randomly throughout the request like this:
312
313```js
314experiment
315 .flow.get('/search', {
316 qs: {
317 'airportcode': '<%= fixtures.airports.random() %>'
318 },
319 fixtures: {
320 airports: require('./airport-codes.json')
321 }
322 });
323```
324
325If you wish, you can then verify the response by looking at the request:
326
327```js
328experiment
329 .flow.get('/search', {
330 qs: {
331 'airportcode': '<%= fixtures.airports.random() %>'
332 },
333 fixtures: {
334 airports: require('./airport-codes.json')
335 }
336 })
337 .verify(function(req, res) {
338 return res.body.airportcode == req.qs.airportcode
339 });
340```
341
342## flowbench.humanize (experimental)
343
344Once you get the stats, you can get a more humanized version of it by passing it through `flowbench.humanize` like this:
345
346```js
347experiment.begin(function(err, stats) {
348 if (err) {
349 throw err;
350 }
351 stats = flowbench.humanize(stats);
352 console.log(JSON.stringify(stats, null, ' '));
353});
354```
355
356# License
357
358ISC
\No newline at end of file