UNPKG

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