Top Banner
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 Edition!
49

Unit Testing Express and Koa Middleware in ES2015

Jan 15, 2017

Download

Software

Morris Singer
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: Unit Testing Express and Koa Middleware in ES2015

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

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

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

MIDDLEWAREBuilding Your Product, One Layer at a Time

Page 5: Unit Testing Express and Koa Middleware in ES2015

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

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

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

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

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

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

A B C

1

D E F

2

G H I

3

GET /

Page 12: Unit Testing Express and Koa Middleware in ES2015

TEST BEHAVIOR

Page 13: Unit Testing Express and Koa Middleware in ES2015

COMMON CHALLENGESOr, Why Node Developers Often Avoid TDD

Page 14: Unit Testing Express and Koa Middleware in ES2015

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

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

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

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

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

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

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

MIDDLEWARE + SEPARATION OF CONCERNS + FLOW CONTROLThe “Eureka” Moment

Page 22: Unit Testing Express and Koa Middleware in ES2015

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

PUTTING IT ALL TOGETHER“Detroit Industry” by Diego Rivera

Page 42: Unit Testing Express and Koa Middleware in ES2015

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

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

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

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

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

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

QUESTIONS

Page 49: Unit Testing Express and Koa Middleware in ES2015

GET IN TOUCH

! @morrissinger

" linkedin.com/in/morrissinger

# morrissinger.com

$ github.com/morrissinger