SlideShare a Scribd company logo
1 of 49
Download to read offline
UNITTESTING NODE.JS
MIDDLEWARE
By Morris Singer
This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.
express and
ES15
Edition!
ABOUT ME
• Senior Software Engineer atVerilume
• I Like:
• Test-Driven Development
• Angular 1 and 2,Aurelia, Ionic, and
React.js, Node.js, and Cordova
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)
MIDDLEWARE
BuildingYour Product, One Layer at aTime
A SIMPLE CASE
One Middleware Per Endpoint
express
app.use(function (req, res, next) {
res.send('hello world');
});
A SIMPLE CASE
“Why is it called ‘Middleware’ anyway?”
app.use(function* (next) {
this.body = 'hello world';
});
• One Middleware Per Endpoint
MORE COMPLEX CASES
Two 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);
MORE COMPLEX CASES
Two Ways of Stacking Middleware
Variadic Iterative
app.use(...middleware);
const middleware = [
function* (next) => {
this.message = 'HELLO WORLD';
return yield next;
},
function* (next) => {
this.body = this.message.toLowerCase();
}
];
middleware.forEach(app.use);
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());
}
);
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();
}
);
A B C
1
D E F
2
G H I
3
GET /
TEST BEHAVIOR
COMMON CHALLENGES
Or,Why Node Developers Often AvoidTDD
HTTP RESPONSETESTS
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();
});
});
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);
ILLUMINATINGTEST 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();
});
});
KNOWING WHENTOTEST
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');
});
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);
});
});
DEALING WITH POLLUTION
How does one reset the data?
express
it('should update the first post', () => {
/* ... */
});
it('should get the first post', () => {
/* ... */
});
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();
});
});
MIDDLEWARE + SEPARATION OF CONCERNS
+ FLOW CONTROL
The “Eureka” Moment
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.
PULL BEHAVIOR INTO
MIDDLEWARE,TESTS
Endpoint
Test
BehaviorBehavior
BehaviorBehavior
Endpoint
TestTest
Old Paradigm
New Paradigm
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);
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);
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
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. */
};
}
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.
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
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. */
}
RETURN CLIENT-SERVER
INTERACTIONTO ENDPOINT
Endpoint
Res
Req
Behavior
Res
Req
Client
Endpoint
Value
Object
Behavior
Value
Object
Client
Old Paradigm
New Paradigm
RETURN CLIENT-SERVER
INTERACTIONTO ENDPOINT
Old 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);
RETURN CLIENT-SERVER
INTERACTIONTO ENDPOINT
Old 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);
RETURN CLIENT-SERVER
INTERACTIONTO ENDPOINT
Old 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 */)
);
RETURN CLIENT-SERVER
INTERACTIONTO ENDPOINT
Old 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);
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
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');
});
});
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)
CO-MOCHA
Enable support for generators in Mocha tests using co.
https://www.npmjs.com/package/co-mocha
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');
});
});
PUTTING IT ALLTOGETHER
“Detroit Industry” by Diego Rivera
Return Client-Server
Interaction to Endpoints
ENDPOINTS
Pull Behavior
into Endpoint
import {behavior} from './behavior.js';
app.use(function (req, res, next) {
behavior()
.then(function () { next(); })
.catch(res.json)
});
express
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
Pull Behavior IntoTests
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
Return Client-Server
Interaction to Endpoints
ENDPOINTS
Pull Behavior
into Endpoint
import {behavior} from './behavior.js';
app.use(function* (next) {
let message = yield behavior();
this.body = message;
});
Use Generators as
Flow Control
BEHAVIOR
export function* behavior (next) {
yield asyncRequest();
return yield next;
};
Pull Behavior IntoTests
TEST
var behavior = require('./behavior.js');
describe('behavior', () => {
it ('resolves under condition X with result Y', function* () {
return Assert.equal(yield behavior(), 'value');
});
});
QUESTIONS
GET INTOUCH
! @morrissinger
" linkedin.com/in/morrissinger
# morrissinger.com
$ github.com/morrissinger

More Related Content

What's hot

Performance Tuning With Oracle ASH and AWR. Part 1 How And What
Performance Tuning With Oracle ASH and AWR. Part 1 How And WhatPerformance Tuning With Oracle ASH and AWR. Part 1 How And What
Performance Tuning With Oracle ASH and AWR. Part 1 How And What
udaymoogala
 
Designing Apache Hudi for Incremental Processing With Vinoth Chandar and Etha...
Designing Apache Hudi for Incremental Processing With Vinoth Chandar and Etha...Designing Apache Hudi for Incremental Processing With Vinoth Chandar and Etha...
Designing Apache Hudi for Incremental Processing With Vinoth Chandar and Etha...
HostedbyConfluent
 
12 o uso do laboratório de informática como ferramenta pedagógica
12  o uso do laboratório de informática como ferramenta pedagógica12  o uso do laboratório de informática como ferramenta pedagógica
12 o uso do laboratório de informática como ferramenta pedagógica
juniorfuleragem
 

What's hot (20)

Scylla Summit 2022: The Future of Consensus in ScyllaDB 5.0 and Beyond
Scylla Summit 2022: The Future of Consensus in ScyllaDB 5.0 and BeyondScylla Summit 2022: The Future of Consensus in ScyllaDB 5.0 and Beyond
Scylla Summit 2022: The Future of Consensus in ScyllaDB 5.0 and Beyond
 
Programa GLAUBER 2023 17-04 B.docx
Programa GLAUBER 2023 17-04 B.docxPrograma GLAUBER 2023 17-04 B.docx
Programa GLAUBER 2023 17-04 B.docx
 
Spark - Alexis Seigneurin (Français)
Spark - Alexis Seigneurin (Français)Spark - Alexis Seigneurin (Français)
Spark - Alexis Seigneurin (Français)
 
O erro e o fracasso escolar
O erro e o fracasso escolarO erro e o fracasso escolar
O erro e o fracasso escolar
 
Oracle Drivers configuration for High Availability, is it a developer's job?
Oracle Drivers configuration for High Availability, is it a developer's job?Oracle Drivers configuration for High Availability, is it a developer's job?
Oracle Drivers configuration for High Availability, is it a developer's job?
 
Performance Tuning With Oracle ASH and AWR. Part 1 How And What
Performance Tuning With Oracle ASH and AWR. Part 1 How And WhatPerformance Tuning With Oracle ASH and AWR. Part 1 How And What
Performance Tuning With Oracle ASH and AWR. Part 1 How And What
 
Database option SDO mismatch: PDB installed version 12.1.0.2.0. CDB installed...
Database option SDO mismatch: PDB installed version 12.1.0.2.0. CDB installed...Database option SDO mismatch: PDB installed version 12.1.0.2.0. CDB installed...
Database option SDO mismatch: PDB installed version 12.1.0.2.0. CDB installed...
 
DB12c: All You Need to Know About the Resource Manager
DB12c: All You Need to Know About the Resource ManagerDB12c: All You Need to Know About the Resource Manager
DB12c: All You Need to Know About the Resource Manager
 
Anexos formulário para reserva do laboratório de informática
Anexos   formulário para reserva do laboratório de informáticaAnexos   formulário para reserva do laboratório de informática
Anexos formulário para reserva do laboratório de informática
 
Oracle GoldenGate
Oracle GoldenGate Oracle GoldenGate
Oracle GoldenGate
 
ODA Backup Restore Utility & ODA Rescue Live Disk
ODA Backup Restore Utility & ODA Rescue Live DiskODA Backup Restore Utility & ODA Rescue Live Disk
ODA Backup Restore Utility & ODA Rescue Live Disk
 
Building Data Product Based on Apache Spark at Airbnb with Jingwei Lu and Liy...
Building Data Product Based on Apache Spark at Airbnb with Jingwei Lu and Liy...Building Data Product Based on Apache Spark at Airbnb with Jingwei Lu and Liy...
Building Data Product Based on Apache Spark at Airbnb with Jingwei Lu and Liy...
 
Using Kafka and Kudu for fast, low-latency SQL analytics on streaming data
Using Kafka and Kudu for fast, low-latency SQL analytics on streaming dataUsing Kafka and Kudu for fast, low-latency SQL analytics on streaming data
Using Kafka and Kudu for fast, low-latency SQL analytics on streaming data
 
Alfabetizar letrando
Alfabetizar letrandoAlfabetizar letrando
Alfabetizar letrando
 
Proposta pedagógica para o laboratório de informática educativa
Proposta pedagógica para o laboratório de informática educativaProposta pedagógica para o laboratório de informática educativa
Proposta pedagógica para o laboratório de informática educativa
 
Designing Apache Hudi for Incremental Processing With Vinoth Chandar and Etha...
Designing Apache Hudi for Incremental Processing With Vinoth Chandar and Etha...Designing Apache Hudi for Incremental Processing With Vinoth Chandar and Etha...
Designing Apache Hudi for Incremental Processing With Vinoth Chandar and Etha...
 
12 o uso do laboratório de informática como ferramenta pedagógica
12  o uso do laboratório de informática como ferramenta pedagógica12  o uso do laboratório de informática como ferramenta pedagógica
12 o uso do laboratório de informática como ferramenta pedagógica
 
A vida e a viagem de trem
A vida e a viagem de tremA vida e a viagem de trem
A vida e a viagem de trem
 
Planejamento_ 1º Bimestre_7ºAno -2023- Português- Anisio.docx
Planejamento_ 1º Bimestre_7ºAno -2023- Português- Anisio.docxPlanejamento_ 1º Bimestre_7ºAno -2023- Português- Anisio.docx
Planejamento_ 1º Bimestre_7ºAno -2023- Português- Anisio.docx
 
Planejamento e avaliação
Planejamento e avaliaçãoPlanejamento e avaliação
Planejamento e avaliação
 

Similar to Unit Testing Express and Koa Middleware in ES2015

How and why i roll my own node.js framework
How and why i roll my own node.js frameworkHow and why i roll my own node.js framework
How and why i roll my own node.js framework
Ben Lin
 
Intro To JavaScript Unit Testing - Ran Mizrahi
Intro To JavaScript Unit Testing - Ran MizrahiIntro To JavaScript Unit Testing - Ran Mizrahi
Intro To JavaScript Unit Testing - Ran Mizrahi
Ran Mizrahi
 
Writing robust Node.js applications
Writing robust Node.js applicationsWriting robust Node.js applications
Writing robust Node.js applications
Tom Croucher
 
node.js practical guide to serverside javascript
node.js practical guide to serverside javascriptnode.js practical guide to serverside javascript
node.js practical guide to serverside javascript
Eldar Djafarov
 

Similar to Unit Testing Express and Koa Middleware in ES2015 (20)

Unit Testing Express Middleware
Unit Testing Express MiddlewareUnit Testing Express Middleware
Unit Testing Express Middleware
 
Middleware.pdf
Middleware.pdfMiddleware.pdf
Middleware.pdf
 
Build Web Apps using Node.js
Build Web Apps using Node.jsBuild Web Apps using Node.js
Build Web Apps using Node.js
 
How and why i roll my own node.js framework
How and why i roll my own node.js frameworkHow and why i roll my own node.js framework
How and why i roll my own node.js framework
 
Building Web Apps with Express
Building Web Apps with ExpressBuilding Web Apps with Express
Building Web Apps with Express
 
Developing web-apps like it's 2013
Developing web-apps like it's 2013Developing web-apps like it's 2013
Developing web-apps like it's 2013
 
RESTful API In Node Js using Express
RESTful API In Node Js using Express RESTful API In Node Js using Express
RESTful API In Node Js using Express
 
JS everywhere 2011
JS everywhere 2011JS everywhere 2011
JS everywhere 2011
 
Going fullstack React(ive) - Paulo Lopes - Codemotion Amsterdam 2017
Going fullstack React(ive) - Paulo Lopes - Codemotion Amsterdam 2017Going fullstack React(ive) - Paulo Lopes - Codemotion Amsterdam 2017
Going fullstack React(ive) - Paulo Lopes - Codemotion Amsterdam 2017
 
Workshop 4: NodeJS. Express Framework & MongoDB.
Workshop 4: NodeJS. Express Framework & MongoDB.Workshop 4: NodeJS. Express Framework & MongoDB.
Workshop 4: NodeJS. Express Framework & MongoDB.
 
Intro To JavaScript Unit Testing - Ran Mizrahi
Intro To JavaScript Unit Testing - Ran MizrahiIntro To JavaScript Unit Testing - Ran Mizrahi
Intro To JavaScript Unit Testing - Ran Mizrahi
 
Writing robust Node.js applications
Writing robust Node.js applicationsWriting robust Node.js applications
Writing robust Node.js applications
 
Module design pattern i.e. express js
Module design pattern i.e. express jsModule design pattern i.e. express js
Module design pattern i.e. express js
 
Ember.js - A JavaScript framework for creating ambitious web applications
Ember.js - A JavaScript framework for creating ambitious web applications  Ember.js - A JavaScript framework for creating ambitious web applications
Ember.js - A JavaScript framework for creating ambitious web applications
 
Intro to Node
Intro to NodeIntro to Node
Intro to Node
 
node.js practical guide to serverside javascript
node.js practical guide to serverside javascriptnode.js practical guide to serverside javascript
node.js practical guide to serverside javascript
 
apidays LIVE Australia 2020 - Building distributed systems on the shoulders o...
apidays LIVE Australia 2020 - Building distributed systems on the shoulders o...apidays LIVE Australia 2020 - Building distributed systems on the shoulders o...
apidays LIVE Australia 2020 - Building distributed systems on the shoulders o...
 
Future Decoded - Node.js per sviluppatori .NET
Future Decoded - Node.js per sviluppatori .NETFuture Decoded - Node.js per sviluppatori .NET
Future Decoded - Node.js per sviluppatori .NET
 
Reactive programming every day
Reactive programming every dayReactive programming every day
Reactive programming every day
 
Workshop 23: ReactJS, React & Redux testing
Workshop 23: ReactJS, React & Redux testingWorkshop 23: ReactJS, React & Redux testing
Workshop 23: ReactJS, React & Redux testing
 

Recently uploaded

%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
masabamasaba
 
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
masabamasaba
 
introduction-to-automotive Andoid os-csimmonds-ndctechtown-2021.pdf
introduction-to-automotive Andoid os-csimmonds-ndctechtown-2021.pdfintroduction-to-automotive Andoid os-csimmonds-ndctechtown-2021.pdf
introduction-to-automotive Andoid os-csimmonds-ndctechtown-2021.pdf
VishalKumarJha10
 
%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...
%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...
%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...
masabamasaba
 

Recently uploaded (20)

AI & Machine Learning Presentation Template
AI & Machine Learning Presentation TemplateAI & Machine Learning Presentation Template
AI & Machine Learning Presentation Template
 
%in Stilfontein+277-882-255-28 abortion pills for sale in Stilfontein
%in Stilfontein+277-882-255-28 abortion pills for sale in Stilfontein%in Stilfontein+277-882-255-28 abortion pills for sale in Stilfontein
%in Stilfontein+277-882-255-28 abortion pills for sale in Stilfontein
 
Direct Style Effect Systems - The Print[A] Example - A Comprehension Aid
Direct Style Effect Systems -The Print[A] Example- A Comprehension AidDirect Style Effect Systems -The Print[A] Example- A Comprehension Aid
Direct Style Effect Systems - The Print[A] Example - A Comprehension Aid
 
%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein
%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein
%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein
 
10 Trends Likely to Shape Enterprise Technology in 2024
10 Trends Likely to Shape Enterprise Technology in 202410 Trends Likely to Shape Enterprise Technology in 2024
10 Trends Likely to Shape Enterprise Technology in 2024
 
%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain
%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain
%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain
 
Introducing Microsoft’s new Enterprise Work Management (EWM) Solution
Introducing Microsoft’s new Enterprise Work Management (EWM) SolutionIntroducing Microsoft’s new Enterprise Work Management (EWM) Solution
Introducing Microsoft’s new Enterprise Work Management (EWM) Solution
 
Architecture decision records - How not to get lost in the past
Architecture decision records - How not to get lost in the pastArchitecture decision records - How not to get lost in the past
Architecture decision records - How not to get lost in the past
 
Define the academic and professional writing..pdf
Define the academic and professional writing..pdfDefine the academic and professional writing..pdf
Define the academic and professional writing..pdf
 
Generic or specific? Making sensible software design decisions
Generic or specific? Making sensible software design decisionsGeneric or specific? Making sensible software design decisions
Generic or specific? Making sensible software design decisions
 
%in Midrand+277-882-255-28 abortion pills for sale in midrand
%in Midrand+277-882-255-28 abortion pills for sale in midrand%in Midrand+277-882-255-28 abortion pills for sale in midrand
%in Midrand+277-882-255-28 abortion pills for sale in midrand
 
%in tembisa+277-882-255-28 abortion pills for sale in tembisa
%in tembisa+277-882-255-28 abortion pills for sale in tembisa%in tembisa+277-882-255-28 abortion pills for sale in tembisa
%in tembisa+277-882-255-28 abortion pills for sale in tembisa
 
%in Hazyview+277-882-255-28 abortion pills for sale in Hazyview
%in Hazyview+277-882-255-28 abortion pills for sale in Hazyview%in Hazyview+277-882-255-28 abortion pills for sale in Hazyview
%in Hazyview+277-882-255-28 abortion pills for sale in Hazyview
 
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
 
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
 
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
 
introduction-to-automotive Andoid os-csimmonds-ndctechtown-2021.pdf
introduction-to-automotive Andoid os-csimmonds-ndctechtown-2021.pdfintroduction-to-automotive Andoid os-csimmonds-ndctechtown-2021.pdf
introduction-to-automotive Andoid os-csimmonds-ndctechtown-2021.pdf
 
%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...
%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...
%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...
 
Right Money Management App For Your Financial Goals
Right Money Management App For Your Financial GoalsRight Money Management App For Your Financial Goals
Right Money Management App For Your Financial Goals
 
Exploring the Best Video Editing App.pdf
Exploring the Best Video Editing App.pdfExploring the Best Video Editing App.pdf
Exploring the Best Video Editing App.pdf
 

Unit Testing Express and Koa Middleware in ES2015

  • 1. UNITTESTING NODE.JS MIDDLEWARE By Morris Singer This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License. express and ES15 Edition!
  • 2. ABOUT ME • Senior Software Engineer atVerilume • I Like: • Test-Driven Development • Angular 1 and 2,Aurelia, Ionic, and React.js, Node.js, and Cordova
  • 3. 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)
  • 5. A SIMPLE CASE One Middleware Per Endpoint express app.use(function (req, res, next) { res.send('hello world'); });
  • 6. A SIMPLE CASE “Why is it called ‘Middleware’ anyway?” app.use(function* (next) { this.body = 'hello world'; }); • One Middleware Per Endpoint
  • 7. MORE COMPLEX CASES Two 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);
  • 8. MORE COMPLEX CASES Two Ways of Stacking Middleware Variadic Iterative app.use(...middleware); const middleware = [ function* (next) => { this.message = 'HELLO WORLD'; return yield next; }, function* (next) => { this.body = this.message.toLowerCase(); } ]; middleware.forEach(app.use);
  • 9. 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()); } );
  • 10. 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(); } );
  • 11. A B C 1 D E F 2 G H I 3 GET /
  • 13. COMMON CHALLENGES Or,Why Node Developers Often AvoidTDD
  • 14. HTTP RESPONSETESTS 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(); }); });
  • 15. 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);
  • 16. ILLUMINATINGTEST 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(); }); });
  • 17. KNOWING WHENTOTEST 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'); });
  • 18. 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); }); });
  • 19. DEALING WITH POLLUTION How does one reset the data? express it('should update the first post', () => { /* ... */ }); it('should get the first post', () => { /* ... */ });
  • 20. 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(); }); });
  • 21. MIDDLEWARE + SEPARATION OF CONCERNS + FLOW CONTROL The “Eureka” Moment
  • 22. 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.
  • 24. 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);
  • 25. 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);
  • 26. 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
  • 27. 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. */ }; }
  • 28. 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.
  • 29. 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
  • 30. 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. */ }
  • 32. RETURN CLIENT-SERVER INTERACTIONTO ENDPOINT Old 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);
  • 33. RETURN CLIENT-SERVER INTERACTIONTO ENDPOINT Old 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);
  • 34. RETURN CLIENT-SERVER INTERACTIONTO ENDPOINT Old 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 */) );
  • 35. RETURN CLIENT-SERVER INTERACTIONTO ENDPOINT Old 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);
  • 36. 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
  • 37. 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'); }); });
  • 38. 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)
  • 39. CO-MOCHA Enable support for generators in Mocha tests using co. https://www.npmjs.com/package/co-mocha
  • 40. 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'); }); });
  • 41. PUTTING IT ALLTOGETHER “Detroit Industry” by Diego Rivera
  • 42. Return Client-Server Interaction to Endpoints ENDPOINTS Pull Behavior into Endpoint import {behavior} from './behavior.js'; app.use(function (req, res, next) { behavior() .then(function () { next(); }) .catch(res.json) }); express
  • 43. 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
  • 44. Pull Behavior IntoTests 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
  • 45. Return Client-Server Interaction to Endpoints ENDPOINTS Pull Behavior into Endpoint import {behavior} from './behavior.js'; app.use(function* (next) { let message = yield behavior(); this.body = message; });
  • 46. Use Generators as Flow Control BEHAVIOR export function* behavior (next) { yield asyncRequest(); return yield next; };
  • 47. Pull Behavior IntoTests TEST var behavior = require('./behavior.js'); describe('behavior', () => { it ('resolves under condition X with result Y', function* () { return Assert.equal(yield behavior(), 'value'); }); });
  • 49. GET INTOUCH ! @morrissinger " linkedin.com/in/morrissinger # morrissinger.com $ github.com/morrissinger