Unit Testing Express and Koa Middleware in ES2015
-
Upload
morris-singer -
Category
Software
-
view
1.613 -
download
1
Transcript of Unit Testing Express and Koa Middleware in ES2015
![Page 1: Unit Testing Express and Koa Middleware in ES2015](https://reader031.fdocuments.in/reader031/viewer/2022021813/587b23831a28ab736c8b719d/html5/thumbnails/1.jpg)
UNIT TESTING NODE.JS MIDDLEWARE
By Morris Singer
This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.
express and
ES15 Editio
n!
![Page 2: Unit Testing Express and Koa Middleware in ES2015](https://reader031.fdocuments.in/reader031/viewer/2022021813/587b23831a28ab736c8b719d/html5/thumbnails/2.jpg)
ABOUT ME
• Senior Software Engineer at Verilume
• I Like:
• Test-Driven Development
• Angular 1 and 2, Aurelia, Ionic, and React.js, Node.js, and Cordova
![Page 3: Unit Testing Express and Koa Middleware in ES2015](https://reader031.fdocuments.in/reader031/viewer/2022021813/587b23831a28ab736c8b719d/html5/thumbnails/3.jpg)
AGENDA• Define middleware and why it isn’t just
a fancy term for controllers or endpoints.
• Review behavior-driven development principles for unit testing.
• Argue why middleware are behavioral units.
• Summarize common challenges testing behavior in Express and Koa.
• Learn and implement a pattern for Express and Koa Middleware.
• Answer questions. (10 minutes)
![Page 4: Unit Testing Express and Koa Middleware in ES2015](https://reader031.fdocuments.in/reader031/viewer/2022021813/587b23831a28ab736c8b719d/html5/thumbnails/4.jpg)
MIDDLEWAREBuilding Your Product, One Layer at a Time
![Page 5: Unit Testing Express and Koa Middleware in ES2015](https://reader031.fdocuments.in/reader031/viewer/2022021813/587b23831a28ab736c8b719d/html5/thumbnails/5.jpg)
A SIMPLE CASEOne Middleware Per Endpoint
express
app.use(function (req, res, next) { res.send('hello world'); });
![Page 6: Unit Testing Express and Koa Middleware in ES2015](https://reader031.fdocuments.in/reader031/viewer/2022021813/587b23831a28ab736c8b719d/html5/thumbnails/6.jpg)
A SIMPLE CASE
“Why is it called ‘Middleware’ anyway?”
app.use(function* (next) { this.body = 'hello world'; });
• One Middleware Per Endpoint
![Page 7: Unit Testing Express and Koa Middleware in ES2015](https://reader031.fdocuments.in/reader031/viewer/2022021813/587b23831a28ab736c8b719d/html5/thumbnails/7.jpg)
MORE COMPLEX CASESTwo Ways of Stacking Middleware
Variadic Iterative
express
const middleware = [ function (req, res, next) { req.message = 'HELLO WORLD'; next(); }, function (req, res, next) { res.send(req.message.toLowerCase()); } ];
middleware.forEach(app.use);app.use(...middleware);
![Page 8: Unit Testing Express and Koa Middleware in ES2015](https://reader031.fdocuments.in/reader031/viewer/2022021813/587b23831a28ab736c8b719d/html5/thumbnails/8.jpg)
MORE COMPLEX CASESTwo Ways of Stacking Middleware
Variadic Iterativeapp.use(...middleware);
const middleware = [ function* (next) => { this.message = 'HELLO WORLD'; return yield next; }, function* (next) => { this.body = this.message.toLowerCase(); } ];
middleware.forEach(app.use);
![Page 9: Unit Testing Express and Koa Middleware in ES2015](https://reader031.fdocuments.in/reader031/viewer/2022021813/587b23831a28ab736c8b719d/html5/thumbnails/9.jpg)
THE MIDDLEWARE STACK
GET
Done
Generate Message
Send Lowercase Message
express
app.use(
function (req, res, next) { req.message = 'HELLO WORLD'; next(); },
function (req, res, next) { res.send(req.message.toLowerCase()); }
);
![Page 10: Unit Testing Express and Koa Middleware in ES2015](https://reader031.fdocuments.in/reader031/viewer/2022021813/587b23831a28ab736c8b719d/html5/thumbnails/10.jpg)
THE MIDDLEWARE STACK
GET
Done
Generate Message
Assign Lowercase to Body
app.use(
function (next) { this.message = 'HELLO WORLD'; return yield next; },
function (next) { this.body = this.message.toLowerCase(); }
);
![Page 11: Unit Testing Express and Koa Middleware in ES2015](https://reader031.fdocuments.in/reader031/viewer/2022021813/587b23831a28ab736c8b719d/html5/thumbnails/11.jpg)
A B C
1
D E F
2
G H I
3
GET /
![Page 12: Unit Testing Express and Koa Middleware in ES2015](https://reader031.fdocuments.in/reader031/viewer/2022021813/587b23831a28ab736c8b719d/html5/thumbnails/12.jpg)
TEST BEHAVIOR
![Page 13: Unit Testing Express and Koa Middleware in ES2015](https://reader031.fdocuments.in/reader031/viewer/2022021813/587b23831a28ab736c8b719d/html5/thumbnails/13.jpg)
COMMON CHALLENGESOr, Why Node Developers Often Avoid TDD
![Page 14: Unit Testing Express and Koa Middleware in ES2015](https://reader031.fdocuments.in/reader031/viewer/2022021813/587b23831a28ab736c8b719d/html5/thumbnails/14.jpg)
HTTP RESPONSE TESTS
What happens when we add a middleware to the stack?express
it('should return a 500 error', (done) => { request({ method: 'POST', url: 'http://localhost:3000/api/endpoint' }, (error, response, body) => { Assert.equal(response.statusCode, 500); done(); }); });
![Page 15: Unit Testing Express and Koa Middleware in ES2015](https://reader031.fdocuments.in/reader031/viewer/2022021813/587b23831a28ab736c8b719d/html5/thumbnails/15.jpg)
TESTING MID-STACK
How do we pull out these anonymous functions?express
const middleware = [ function (req, res, next) { req.message = 'HELLO WORLD'; next(); }, function (req, res, next) { res.send(req.message.toLowerCase()); } ];
middleware.forEach(app.use);
![Page 16: Unit Testing Express and Koa Middleware in ES2015](https://reader031.fdocuments.in/reader031/viewer/2022021813/587b23831a28ab736c8b719d/html5/thumbnails/16.jpg)
ILLUMINATING TEST FAILURES
What happens if next() is not called?express
import {httpMocks} from 'node-mocks-http';
it('should call next()', (done) => { var req = httpMocks.createRequest(), res = httpMocks.createResponse();
middleware(req, res, () => { done(); }); });
![Page 17: Unit Testing Express and Koa Middleware in ES2015](https://reader031.fdocuments.in/reader031/viewer/2022021813/587b23831a28ab736c8b719d/html5/thumbnails/17.jpg)
KNOWING WHEN TO TEST
When is the assertion run?express
import {httpMocks} from 'node-mocks-http';
it('should call next()', () => { var req = httpMocks.createRequest(), res = httpMocks.createResponse();
middleware(req, res);
return Assert.equal(req.foo, 'bar'); });
![Page 18: Unit Testing Express and Koa Middleware in ES2015](https://reader031.fdocuments.in/reader031/viewer/2022021813/587b23831a28ab736c8b719d/html5/thumbnails/18.jpg)
TESTING WITH DATA
Where do data come from?express
app.get('path/to/post', function (req, res, next) { Post.findOne(params).exec(function (err, post) { res.json(post); }); });
![Page 19: Unit Testing Express and Koa Middleware in ES2015](https://reader031.fdocuments.in/reader031/viewer/2022021813/587b23831a28ab736c8b719d/html5/thumbnails/19.jpg)
DEALING WITH POLLUTION
How does one reset the data?express
it('should update the first post', () => { /* ... */ });
it('should get the first post', () => { /* ... */ });
![Page 20: Unit Testing Express and Koa Middleware in ES2015](https://reader031.fdocuments.in/reader031/viewer/2022021813/587b23831a28ab736c8b719d/html5/thumbnails/20.jpg)
MOCKING DEPENDENCIES
How does one cut out the external data source?express
app.get('endpoint', function (req, res, next) { request({ method: 'GET', url: 'http://example.com/api/call' }, (error, response, body) => { req.externalData = body; next(); }); });
![Page 21: Unit Testing Express and Koa Middleware in ES2015](https://reader031.fdocuments.in/reader031/viewer/2022021813/587b23831a28ab736c8b719d/html5/thumbnails/21.jpg)
MIDDLEWARE + SEPARATION OF CONCERNS + FLOW CONTROLThe “Eureka” Moment
![Page 22: Unit Testing Express and Koa Middleware in ES2015](https://reader031.fdocuments.in/reader031/viewer/2022021813/587b23831a28ab736c8b719d/html5/thumbnails/22.jpg)
OVERVIEW
• Pull behavior into middleware and tests.
• Use promises or generators as flow control.
• Return client-server interaction to endpoint.
• Use promises or generators with Mocha.
![Page 23: Unit Testing Express and Koa Middleware in ES2015](https://reader031.fdocuments.in/reader031/viewer/2022021813/587b23831a28ab736c8b719d/html5/thumbnails/23.jpg)
PULL BEHAVIOR INTO MIDDLEWARE, TESTS
Endpoint
Test
BehaviorBehavior
BehaviorBehavior
Endpoint
TestTest
Old Paradigm
New Paradigm
![Page 24: Unit Testing Express and Koa Middleware in ES2015](https://reader031.fdocuments.in/reader031/viewer/2022021813/587b23831a28ab736c8b719d/html5/thumbnails/24.jpg)
PULL BEHAVIOR INTO ENDPOINTS
Old Paradigm New Paradigm
N.B.: This only looks like a lot more code…express
const middleware = [ function (req, res, next) { /* Behavior */ }, function (req, res, next) { /* Behavior */ } ];
app.use(...middleware);
const behavior = { first: function () {}, second: function () {} };
const middleware = [ function (req, res, next) { behavior.first(); next(); }, function (req, res, next) { behavior.second(); next(); } ];
app.use(...middleware);
![Page 25: Unit Testing Express and Koa Middleware in ES2015](https://reader031.fdocuments.in/reader031/viewer/2022021813/587b23831a28ab736c8b719d/html5/thumbnails/25.jpg)
PULL BEHAVIOR INTO ENDPOINTS
Old Paradigm New Paradigm
const middleware = [ function* (next) { /* Behavior */ }, function* (next) { /* Behavior */ } ];
app.use(...middleware);
const behavior = { first: function* () {}, second: function* () {} };
const middleware = [ function* (next) { yield behavior.first(); return yield next; }, function* (next) { yield behavior.second(); return next; } ];
app.use(...middleware);
![Page 26: Unit Testing Express and Koa Middleware in ES2015](https://reader031.fdocuments.in/reader031/viewer/2022021813/587b23831a28ab736c8b719d/html5/thumbnails/26.jpg)
USE PROMISES AS FLOW CONTROL
• Clean, standardized interface between asynchronous behavior and endpoints.
• Both endpoints and tests can leverage the same mechanism in the behavior for serializing logic.
express
![Page 27: Unit Testing Express and Koa Middleware in ES2015](https://reader031.fdocuments.in/reader031/viewer/2022021813/587b23831a28ab736c8b719d/html5/thumbnails/27.jpg)
USE PROMISES AS FLOW CONTROL
Old Paradigm
New Paradigm
express
export function middleware (req, res, next) {
/* Define behavior and call res.json(), next(), etc. */
};
export function behavior () { return new Promise((resolve, reject) => { /* Define behavior and resolve or reject promise. */ }; }
![Page 28: Unit Testing Express and Koa Middleware in ES2015](https://reader031.fdocuments.in/reader031/viewer/2022021813/587b23831a28ab736c8b719d/html5/thumbnails/28.jpg)
USE GENERATORS (WITH CO) AS FLOW CONTROL
• Same interface between asynchronous behavior and middleware as already used between successive middleware.
• Both endpoints and tests can leverage the same mechanism in the behavior for serializing logic.
![Page 29: Unit Testing Express and Koa Middleware in ES2015](https://reader031.fdocuments.in/reader031/viewer/2022021813/587b23831a28ab736c8b719d/html5/thumbnails/29.jpg)
CO
Generator based control flow goodness for nodejs and the browser, using promises, letting you write non-blocking code in a nice-ish way.
https://www.npmjs.com/package/co
![Page 30: Unit Testing Express and Koa Middleware in ES2015](https://reader031.fdocuments.in/reader031/viewer/2022021813/587b23831a28ab736c8b719d/html5/thumbnails/30.jpg)
USE GENERATORS AS LINK BETWEEN MIDDLEWARE AND ENDPOINTS
Old Paradigm
New Paradigm
export function* middleware (next) {
/* Call with assigned context and leverage behavior on the Koa context, yield next, etc.*/
};
export function* behavior () { /* Define behavior and yield values. */ }
![Page 31: Unit Testing Express and Koa Middleware in ES2015](https://reader031.fdocuments.in/reader031/viewer/2022021813/587b23831a28ab736c8b719d/html5/thumbnails/31.jpg)
RETURN CLIENT-SERVER INTERACTION TO ENDPOINT
Endpoint
Res
Req
Behavior
Res
ReqClient
Endpoint
Value
Object
Behavior
Value
ObjectClient
Old Paradigm
New Paradigm
![Page 32: Unit Testing Express and Koa Middleware in ES2015](https://reader031.fdocuments.in/reader031/viewer/2022021813/587b23831a28ab736c8b719d/html5/thumbnails/32.jpg)
RETURN CLIENT-SERVER INTERACTION TO ENDPOINTOld Paradigm New Paradigm
express
const middleware = [ function (req, res, next) {}, function (req, res, next) {} ];
app.use(...middleware);
const behavior = [ function () {}, function () {} ];
const middleware = [ function (req, res, next) { behavior[0](/* Pass objects, values */) .then(function () { next(); }) .catch(res.json); }, function (req, res, next) { behavior[1](/* Pass objects, values */) .then(function () { next(); }) .catch(res.json); } ];
app.use(...middleware);
![Page 33: Unit Testing Express and Koa Middleware in ES2015](https://reader031.fdocuments.in/reader031/viewer/2022021813/587b23831a28ab736c8b719d/html5/thumbnails/33.jpg)
RETURN CLIENT-SERVER INTERACTION TO ENDPOINTOld Paradigm New Paradigm
express
const middleware = [ function (req, res, next) {}, function (req, res, next) {} ];
app.use(...middleware);
const behavior = [ function () {}, function () {} ];
const middleware = behavior.map((func) => { return function (req, res, next) { func() .then(function () { next(); }) .catch(res.json); } };
app.use(...middleware);
![Page 34: Unit Testing Express and Koa Middleware in ES2015](https://reader031.fdocuments.in/reader031/viewer/2022021813/587b23831a28ab736c8b719d/html5/thumbnails/34.jpg)
RETURN CLIENT-SERVER INTERACTION TO ENDPOINTOld Paradigm New Paradigm
express
const middleware = [ function (req, res, next) {}, function (req, res, next) {} ];
app.use(...middleware);
const behavior = [ function () {}, function () {} ];
const middleware = behavior.map((func) => { return function(args) { return function (req, res, next) { func() .then(function () { next(); }) .catch(res.json); } } };
app.use( middleware[0](/* Pass objects, values */), middleware[1](/* Pass objects, values */) );
![Page 35: Unit Testing Express and Koa Middleware in ES2015](https://reader031.fdocuments.in/reader031/viewer/2022021813/587b23831a28ab736c8b719d/html5/thumbnails/35.jpg)
RETURN CLIENT-SERVER INTERACTION TO ENDPOINTOld Paradigm New Paradigm
const middleware = [ function* (next) {}, function* (next) {} ];
app.use(...middleware);
const behavior = [ function* () {}, function* () {} ];
const middleware = [ function* (next) { yield behavior[0](/* Pass objects, values */); return yield next; }, function* (next) { yield behavior[1](/* Pass objects, values */); return yield next; } ];
app.use(...middleware);
![Page 36: Unit Testing Express and Koa Middleware in ES2015](https://reader031.fdocuments.in/reader031/viewer/2022021813/587b23831a28ab736c8b719d/html5/thumbnails/36.jpg)
USING PROMISES WITH MOCHA
We need:
• A test framework syntax that facilitates easy async testing. (Supported natively in Mocha since 1.18.0)
• An assertion syntax that we are familiar with. (Assert)
• A set of assertions that facilitate easily writing tests of promises. (assertPromise)
express
![Page 37: Unit Testing Express and Koa Middleware in ES2015](https://reader031.fdocuments.in/reader031/viewer/2022021813/587b23831a28ab736c8b719d/html5/thumbnails/37.jpg)
USING PROMISES WITH MOCHA (ASSERT_PROMISE)
Old Paradigm
New Paradigm
express
describe('behavior', () => { it ('resolves under condition X with result Y', (done) => { behavior().then(function (done) { /* Assert here. */ }).finally(done); }); });
import {assertPromise} from 'assert-promise';
describe('behavior', () => { it ('resolves under condition X with result Y', () => { return assertPromise.equal(behavior(), 'value'); }); });
![Page 38: Unit Testing Express and Koa Middleware in ES2015](https://reader031.fdocuments.in/reader031/viewer/2022021813/587b23831a28ab736c8b719d/html5/thumbnails/38.jpg)
USING GENERATORS WITH MOCHA
We need:
• Use the same async flow that Koa leverages (ES15 generators and co)
• An assertion syntax that we are familiar with. (Assert)
• Mocha syntax that facilitates easily writing tests of generators with co. (co-mocha)
![Page 39: Unit Testing Express and Koa Middleware in ES2015](https://reader031.fdocuments.in/reader031/viewer/2022021813/587b23831a28ab736c8b719d/html5/thumbnails/39.jpg)
CO-MOCHA
Enable support for generators in Mocha tests using co.
https://www.npmjs.com/package/co-mocha
![Page 40: Unit Testing Express and Koa Middleware in ES2015](https://reader031.fdocuments.in/reader031/viewer/2022021813/587b23831a28ab736c8b719d/html5/thumbnails/40.jpg)
USING PROMISES WITH MOCHA (CO-MOCHA)
Old Paradigm(No Co-Mocha)
New Paradigm
describe('behavior', () => { it ('resolves under condition X with result Y', (done) => { behavior().then(function () { /* Assert here. */ }).finally(done); }); });
describe('behavior', () => { it ('resolves under condition X with result Y', function* () { return Assert.equal(yield behavior(), 'value'); }); });
![Page 41: Unit Testing Express and Koa Middleware in ES2015](https://reader031.fdocuments.in/reader031/viewer/2022021813/587b23831a28ab736c8b719d/html5/thumbnails/41.jpg)
PUTTING IT ALL TOGETHER“Detroit Industry” by Diego Rivera
![Page 42: Unit Testing Express and Koa Middleware in ES2015](https://reader031.fdocuments.in/reader031/viewer/2022021813/587b23831a28ab736c8b719d/html5/thumbnails/42.jpg)
Return Client-ServerInteraction to Endpoints
ENDPOINTS
Pull Behaviorinto Endpoint
import {behavior} from './behavior.js'; app.use(function (req, res, next) { behavior() .then(function () { next(); }) .catch(res.json) });
express
![Page 43: Unit Testing Express and Koa Middleware in ES2015](https://reader031.fdocuments.in/reader031/viewer/2022021813/587b23831a28ab736c8b719d/html5/thumbnails/43.jpg)
Use Promise as Flow Control
BEHAVIOR
export function behavior (req, res, next) {
return new Promise(function (resolve, reject) { /* Define behavior and resolve or reject. */ }
};
express
![Page 44: Unit Testing Express and Koa Middleware in ES2015](https://reader031.fdocuments.in/reader031/viewer/2022021813/587b23831a28ab736c8b719d/html5/thumbnails/44.jpg)
Pull Behavior Into Tests
TEST
Use Promises with Mochaimport {assertPromise} from "assert-promise";
var behavior = require('./behavior.js');
describe('behavior', () => { it ('resolves under condition X with result Y', () => { return assertPromise.equal(behavior(), 'value'); }); });
express
![Page 45: Unit Testing Express and Koa Middleware in ES2015](https://reader031.fdocuments.in/reader031/viewer/2022021813/587b23831a28ab736c8b719d/html5/thumbnails/45.jpg)
Return Client-ServerInteraction to Endpoints
ENDPOINTS
Pull Behaviorinto Endpoint
import {behavior} from './behavior.js'; app.use(function* (next) { let message = yield behavior(); this.body = message; });
![Page 46: Unit Testing Express and Koa Middleware in ES2015](https://reader031.fdocuments.in/reader031/viewer/2022021813/587b23831a28ab736c8b719d/html5/thumbnails/46.jpg)
Use Generators as Flow Control
BEHAVIOR
export function* behavior (next) {
yield asyncRequest(); return yield next;
};
![Page 47: Unit Testing Express and Koa Middleware in ES2015](https://reader031.fdocuments.in/reader031/viewer/2022021813/587b23831a28ab736c8b719d/html5/thumbnails/47.jpg)
Pull Behavior Into Tests
TEST
var behavior = require('./behavior.js');
describe('behavior', () => { it ('resolves under condition X with result Y', function* () { return Assert.equal(yield behavior(), 'value'); }); });
![Page 48: Unit Testing Express and Koa Middleware in ES2015](https://reader031.fdocuments.in/reader031/viewer/2022021813/587b23831a28ab736c8b719d/html5/thumbnails/48.jpg)
QUESTIONS
![Page 49: Unit Testing Express and Koa Middleware in ES2015](https://reader031.fdocuments.in/reader031/viewer/2022021813/587b23831a28ab736c8b719d/html5/thumbnails/49.jpg)
GET IN TOUCH
! @morrissinger
" linkedin.com/in/morrissinger
# morrissinger.com
$ github.com/morrissinger