SlideShare a Scribd company logo
1 of 58
Download to read offline
express + mocha 
UNIT TESTING EXPRESS 
MIDDLEWARE 
By Morris Singer 
This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.
ABOUT ME 
• Senior Software Engineer 
Cengage Learning 
• Expertise: 
• Sencha Touch 
• Angular.js and Node.js 
• Cordova / PhoneGap 
• Ruby on Rails
AGENDA 
• Define Express Middleware and why it isn’t just 
a fancy term for controllers or endpoints. 
• Review behavior-driven development principles 
for unit testing. 
• Argue why Express Middleware are behavioral 
units. 
• Summarize common challenges testing behavior 
in Express. 
• Review Promises with the Q.js library. 
• Learn and implement a pattern for Promise-based 
Express Middleware. 
• Build tests for common scenarios using Mocha, 
Chai, Chai as Promised, and Mockgoose. 
• Answer questions. (10 minutes)
EXPRESS 
MIDDLEWARE 
Building Your Product, One Layer at a Time
A SIMPLE CASE 
One Middleware Per Endpoint 
app.get('hello.txt', function (req, res, next) { 
res.send(200, 'Hello World!'); 
}); 
“Why is it called ‘Middleware’ anyway?”
MORE COMPLEX CASES 
Two Ways of Stacking Middleware 
app.get('hello.txt', 
function (req, res, next) { 
req.message = 'Hello World!'; 
next(); 
}, 
function (req, res, next) { 
res.send(200, req.message); 
} 
); 
app.get('hello.txt', 
function (req, res, next) { 
req.message = 'Hello World!'; 
next(); 
}); 
! 
app.get('hello.txt', 
function (req, res, next) { 
res.send(200, req.message); 
});
THE MIDDLEWARE STACK 
app.get('hello.txt', 
function (req, res, next) { 
req.message = 'Hello World!'; 
next(); 
}, 
function (req, res, next) { 
res.send(200, req.message); 
} 
); 
GET 
generateMessage 
sendMessage 
res.send
TEST BEHAVIOR
MIDDLEWARE 
IS BEHAVIOR 
Middleware: 
• Define reusable components. 
• Create, modify, and store public 
variables. 
• Send responses to clients. 
• Comprise node packages.
COMMON CHALLENGES 
Or, Why Back End Node Developers Often Avoid TDD
HTTP RESPONSE TESTS 
it('should return a 500 error', function (done){ 
request({ 
method: 'POST', 
url: 'http://localhost:3000/api/endpoint' 
}, function (error, response, body){ 
expect(response.statusCode).to.equal(500); 
done(); 
}); 
}); 
What happens when we add a middleware to the stack?
TESTING MID-STACK 
app.get('hello.txt', 
function (req, res, next) { 
req.message = 'Hello World!'; 
next(); 
}); 
! 
app.get('hello.txt', 
function (req, res, next) { 
res.send(200, req.message); 
}); 
How do we pull out these anonymous functions?
ILLUMINATING TEST FAILURES 
var httpMocks = require('node-mocks-http'); 
! 
it('should call next()', function (done){ 
var req = httpMocks.createRequest(), 
res = httpMocks.createResponse(); 
! 
middleware(req, res, function () { 
done(); 
}); 
}); 
What happens if next() is not called?
KNOWING WHEN TO TEST 
var httpMocks = require('node-mocks-http'); 
! 
it('should call next()', function (done){ 
var req = httpMocks.createRequest(), 
res = httpMocks.createResponse(); 
! 
middleware(req, res); 
! 
expect(req.foo).to.equal('bar'); 
}); 
When is the assertion run?
TESTING WITH DATA 
app.get('path/to/post', function (req, res, next) { 
Post.findOne(params).exec(function (err, post){ 
res.json(200, post); 
}); 
}); 
Where do data come from?
DEALING WITH POLLUTION 
it('should update the first post', function (){ 
/* ... */ 
}); 
! 
it('should get the first post', function (){ 
/* ... */ 
}); 
How does one reset the data?
MOCKING DEPENDENCIES 
app.get('endpoint', function (req, res, next) { 
request({ 
method: 'GET', 
url: 'http://example.com/api/call' 
}, function (error, response, body) { 
req.externalData = body; 
next(); 
}); 
}); 
How does one cut out the external data source?
WRITING ROBUST TESTS 
app.get('hello.txt', 
function (req, res, next) { 
req.message = 'Hello World!'; 
next(); 
}); 
! 
app.get('hello.txt', 
function (req, res, next) { 
res.send(200, req.message); 
}); 
What if someone adds a middleware?
PROMISES 
Links in a Chain of Async Operations
PYRAMID OF DOOM 
queryDatabase(params, function (result) { 
makeRequestOfThirdPartyService(result, function (result) { 
writeFile(result, function (handle) { 
sendFileOverHttp(handle, function (result) { 
}, 
function (err) { 
// Handle Error 
}); 
}, 
function (err) { 
// Handle Error 
}); 
}, 
function (err) { 
// Handle Error 
}); 
});
PROMISES TO THE RESCUE 
queryDatabase() 
.then(makeRequestOfThirdPartyService) 
.then(updateDatabase) 
.then(writeFile) 
.then(sendFileOverHttp) 
.catch(function (err) { 
// Handle Errors 
}).done();
WHAT IS A PROMISE 
A promise is: 
• a delegate 
• for an asynchronous action 
• that: 
• collects references to callbacks 
• maintains state, and 
• provides a mechanism for chaining.
THEN, CATCH, FINALLY, DONE 
myPromise() 
.then(function (result) { 
! 
}) 
.catch(function (err) { 
! 
}) 
.finally(function () { 
! 
}) 
.done(); 
Data sent, received, 
read, written, etc. 
Problems 
No matter what
THE FLIP SIDE 
var Q = require('q'); 
! 
function myPromise() { 
var deferred = Q.defer(); 
! 
if (conditionX) { 
! 
deferred.resolve('Result'); 
! 
} else { 
! 
deferred.reject(new Error()); 
! 
} 
! 
return deferred.promise; 
} 
Triggers then(). 
Passes ‘Result’ 
Triggers catch(). 
Passes new Error()
PUTTING IT ALL TOGETHER 
var Q = require('q'); 
! 
function myPromise() { 
var deferred = Q.defer(); 
! 
if (conditionX) { 
! 
! 
} else { 
! 
! 
} 
! 
return deferred.promise; 
} 
myPromise() ! 
.then(function (result) { 
! 
}) ! 
.catch(function (err) { 
! 
}) ! 
.finally(function () { 
! 
}) ! 
.done(); 
deferred.resolve(‘Result’); 
deferred.reject(new Error());
THE LIFE OF A PROMISE 
Pending 
Fulfilled 
then() 
finally() 
Rejected 
catch() 
finally()
THE PROMISE CHAIN 
Start a new promise chain Continue the chain End the chain 
.then() 
.catch() 
.finally() 
Q.defer().promise 
Q.when() 
Q.promise() 
Q.fcall() 
.done() 
Return a promise
IN PRACTICE 
Promise B Promise D Promise F 
Q.promise() 
.then().then().then().catch().finally().done() 
Promise A Promise C Promise E
DO NOT BREAK 
YOUR CHAINS 
Otherwise, your user may be left hanging…
NOT BREAKING CHAINS 
var Q = require('q'); 
! 
Q.when(function () {}) 
.then(function (result) { 
var deferred = Q.defer(); 
! 
/* Do async and call deferred.resolve() 
and deferred.reject(). */ 
! 
return deferred.promise; 
}) 
.then(function (result) { 
var deferred = Q.defer(); 
! 
/* Do async and call deferred.resolve() 
and deferred.reject(). */ 
! 
return deferred.promise; 
}) 
.catch(function (err) { 
! 
}) 
.done(); 
Resolving here 
calls the referenced function, passing the result as an argument. 
Rejections of either promise result in the referenced function called with err 
and uncaught rejections are thrown as errors here.
Always return a promise or call done(). Period.
EXPRESS + Q 
The “Eureka” Moment
OVERVIEW 
• Pull middleware into endpoints and tests. 
• Mock req and res. 
• Use promises as link between middleware and endpoints. 
• Return client-server interaction to endpoint. 
• Use promises with Mocha.
PULL MIDDLEWARE INTO 
ENDPOINTS, TESTS 
Endpoint 
Middleware Middleware 
Test 
Endpoint 
Middleware Middleware 
Test Test 
! 
! 
Old Paradigm 
" 
! 
New Paradigm
PULL MIDDLEWARE INTO 
ENDPOINTS, TESTS 
app.get('example/uri', function (req, res, next) { 
/* Middleware implementation */ 
}, function (req, res, next) { 
/* Middleware implementation */ 
}); 
var middleware = { 
first: function (req, res, next) {}, 
second: function (req, res, next) {} 
}; 
app.get('example/uri', 
middleware.first, 
middleware.second); 
! 
! 
Old Paradigm 
" 
! 
New Paradigm
MOCK REQ, RES 
• We need a way to call our 
middleware functions 
directly. 
• Our middleware functions 
expect req and res to be 
passed as arguments. 
• So, we mock req and res. 
module npm 
node-mocks-http https://www.npmjs.org/ 
package/node-mocks-http 
express-mocks-http https://www.npmjs.org/ 
package/express-mocks-http
MOCK REQ, RES 
it ('should do something', function (done) { 
var requestParams = { uri: 'http://path.to/endpoint', 
method: 'POST' }; 
! 
request(requestParams, function (error, response, body) { 
expect(response.body).to.equal( /* Expected Data */ ); 
done(); 
}); 
}); 
it ('resolves under condition X with result Y', function () { 
! 
var req = httpMocks.createRequest(), 
res = httpMocks.createResponse(); 
! 
/* Call middleware(req, res) and assert. */ 
! 
}); 
! 
! 
Old Paradigm 
" 
! 
New Paradigm
USE PROMISES AS LINK BETWEEN 
MIDDLEWARE AND ENDPOINTS 
• Clean, standardized 
interface between 
asynchronous middleware 
and endpoints. 
• Both endpoints and tests 
can leverage the same 
mechanism in the 
middleware for serializing 
logic. 
then
USE PROMISES AS LINK BETWEEN 
MIDDLEWARE AND ENDPOINTS 
module.exports = function (req, res, next) { 
! 
/* Define middleware behavior and 
call res.json(), next(), etc. */ 
}; 
var Q = require('q'); 
module.exports = function (req, res) { 
var deferred = Q.defer(); 
/* Define middleware behavior and 
resolve or reject promise. */ 
return deferred.promise; 
}; 
! 
! 
Old Paradigm 
" 
! 
New Paradigm
RETURN CLIENT-SERVER 
INTERACTION TO ENDPOINT 
Endpoint 
Req 
Res 
Middleware 
Req 
Res 
Client 
Endpoint 
Req 
Res 
Middleware 
Req 
Res 
Client 
! 
! 
Old Paradigm 
" 
! 
New Paradigm
RETURN CLIENT-SERVER 
INTERACTION TO ENDPOINT 
var middleware = { 
first: function (req, res, next) {}, 
second: function (req, res, next) {} 
}; 
app.get('example/uri', 
middleware.first, 
middleware.second); 
var middleware = require('./middleware.js'); 
app.get('example/uri', function (req, res, next) { 
middleware.first(req, res) 
.then(function () { next(); }) 
.catch(res.json) 
.done(); 
}, middleware.second(req, res) 
.then(function () { next(); }) 
.catch(res.json) 
.done(); 
}); 
! 
! 
Old Paradigm 
" 
! 
New Paradigm
USING PROMISES WITH 
MOCHA (CHAI-AS-PROMISED) 
We need: 
• A test framework syntax that 
facilitates easy async testing. 
mocha 
(Supported natively in Mocha 
since 1.18.0) 
• An assertion syntax that we are 
familiar with. (Chai) 
• A set of assertions that facilitate 
easily writing tests of promises. 
(Chai-As-Promised) then
USING PROMISES WITH 
MOCHA (CHAI-AS-PROMISED) 
describe('middleware', function () { 
it ('resolves under condition X with result Y', function () { 
! 
var req = httpMocks.createRequest(), 
res = httpMocks.createResponse(); 
! 
middleware(req, res).then(function (done) { 
/* Assert here. */ 
}).finally(done).done(); 
! 
}); 
! 
! 
Old Paradigm 
" 
! 
New Paradigm 
describe('middleware', function () { 
it ('resolves under condition X with result Y', function () { 
! 
var req = httpMocks.createRequest(), 
res = httpMocks.createResponse(); 
! 
return expect(middleware(req, res)).to.eventually.equal('value'); 
! 
});
THE NEW PARADIGM 
Looking at the Whole Picture
ENDPOINTS 
Pull Middleware 
into Endpoint 
Return Client-Server 
Interaction to Endpoints 
var middleware = require('./middleware.js'); 
app.get('example/uri', function (req, res, next) { 
middleware(req, res) 
.then(function () { next(); }) 
.catch(res.json) 
.done(); 
});
MIDDLEWARE 
Use Promise as Link 
Between Middleware and 
Endpoints 
var Q = require('q'); 
module.exports = function (req, res) { 
var deferred = Q.defer(); 
/* Define middleware behavior and 
resolve or reject promise. */ 
return deferred.promise; 
};
Pull Middleware Into Tests 
TEST 
Use Promises with Mocha 
var httpMocks = require('node-mocks-http'), 
chai = require('chai'), 
chaiAsPromised = require('chai-as-promised'); 
chai.use(chaiAsPromised); 
Mock Req, Res 
var middleware = require('path/to/middleware'); 
var req, res; 
beforeEach(function (done) { 
req = httpMocks.createRequest(), 
res = httpMocks.createResponse(); 
}); 
describe('middleware', function () { 
it ('resolves under condition X with result Y', function () { 
return expect(middleware(req, res)).to.be.fulfilled.then(function () { 
/* Assert */ 
}); 
}); 
it ('rejects under condition X with error Y', function () { 
return expect(middleware(req, res)).to.be.rejectedWith('Error String'); 
}); 
});
TESTING WITH DATA 
Mocking Mongoose and Using Fixtures to Build a Robust 
and Effective Test Suite
THE PROBLEM WITH DATA 
We need a solution where: 
• Testing does not depend on the environment, 
• Data travel with the repo, 
• The database can easily be reset to an initial data 
set.
(MONGODB + MONGOOSE)* 
* Solutions are available for other setups. You can also roll your own, 
without too much heartache.
THE HIGH LEVEL 
• Mock MongoDB with in-memory database that can be 
reset between tests and thrown away after tests run. 
(Mockgoose) 
• Write test data as code that can move with the repo. 
(Fixtures) 
• Build test harness that loads fixtures into mock 
database before tests run.
MOCKING MONGOOSE 
var mongoose = require('mongoose'); 
var mockgoose = require('mockgoose'); 
mockgoose(mongoose);
CODING DATA IN FIXTURES 
module.exports.User = [ 
{ name: 'Maeby' }, 
{ name: 'George Michael' } 
];
LOADING FIXTURES 
var loader = require('pow-mongoose-fixtures'); 
! 
var users = require('users.js'); /* User fixtures */ 
! 
beforeEach(function (done) { 
loader.load(users); 
done(); 
}); 
! 
/* Build Tests Here */
TDD EXERCISES 
Use TDD in Pairs to Complete the 
Accompanying TDD / Express Exercises
QUESTIONS
IMPROVEMENTS? 
• There are still some shortcomings in this approach, 
though it is better than other approaches I have seen. 
• Particularly, there are still some failure modes that will 
just timeout. 
• If you can improve on this pattern, PLEASE let me 
know!
GET IN TOUCH 
# @morrissinger 
$ linkedin.com/in/morrissinger 
% morrissinger.com 
& github.com/morrissinger

More Related Content

What's hot

Developing with the Modern App Stack: MEAN and MERN (with Angular2 and ReactJS)
Developing with the Modern App Stack: MEAN and MERN (with Angular2 and ReactJS)Developing with the Modern App Stack: MEAN and MERN (with Angular2 and ReactJS)
Developing with the Modern App Stack: MEAN and MERN (with Angular2 and ReactJS)MongoDB
 
Business Transactions with AppDynamics
Business Transactions with AppDynamicsBusiness Transactions with AppDynamics
Business Transactions with AppDynamicsAppDynamics
 
Software Requirements Specification on Pubg Gaming App (SRS on PUBG)
Software Requirements Specification on Pubg Gaming App (SRS on PUBG)Software Requirements Specification on Pubg Gaming App (SRS on PUBG)
Software Requirements Specification on Pubg Gaming App (SRS on PUBG)KAUSHAL KUMAR JHA
 
Event Driven Software Architecture Pattern
Event Driven Software Architecture PatternEvent Driven Software Architecture Pattern
Event Driven Software Architecture Patternjeetendra mandal
 
Active Directory Single Sign-On with IBM
Active Directory Single Sign-On with IBMActive Directory Single Sign-On with IBM
Active Directory Single Sign-On with IBMVan Staub, MBA
 
Security Exploit of Business Logic Flaws, Business Logic Attacks
Security Exploit of Business Logic Flaws, Business Logic AttacksSecurity Exploit of Business Logic Flaws, Business Logic Attacks
Security Exploit of Business Logic Flaws, Business Logic AttacksMarco Morana
 
Rest API Security
Rest API SecurityRest API Security
Rest API SecurityStormpath
 
Cross Site Request Forgery Vulnerabilities
Cross Site Request Forgery VulnerabilitiesCross Site Request Forgery Vulnerabilities
Cross Site Request Forgery VulnerabilitiesMarco Morana
 
LoadRunner Performance Testing
LoadRunner Performance TestingLoadRunner Performance Testing
LoadRunner Performance TestingAtul Pant
 
The importance of normalizing your security data to ECS
The importance of normalizing your security data to ECSThe importance of normalizing your security data to ECS
The importance of normalizing your security data to ECSElasticsearch
 
security misconfigurations
security misconfigurationssecurity misconfigurations
security misconfigurationsMegha Sahu
 
WebLogic authentication debugging
WebLogic authentication debuggingWebLogic authentication debugging
WebLogic authentication debuggingMaarten Smeets
 
Top 10 Web Application vulnerabilities
Top 10 Web Application vulnerabilitiesTop 10 Web Application vulnerabilities
Top 10 Web Application vulnerabilitiesTerrance Medina
 
12 Steps to API Load Testing with Apache JMeter
12 Steps to API Load Testing with Apache JMeter12 Steps to API Load Testing with Apache JMeter
12 Steps to API Load Testing with Apache JMeterWSO2
 
Enable Authentication and Authorization with Azure Active Directory and Sprin...
Enable Authentication and Authorization with Azure Active Directory and Sprin...Enable Authentication and Authorization with Azure Active Directory and Sprin...
Enable Authentication and Authorization with Azure Active Directory and Sprin...VMware Tanzu
 
Logical Attacks(Vulnerability Research)
Logical Attacks(Vulnerability Research)Logical Attacks(Vulnerability Research)
Logical Attacks(Vulnerability Research)Ajay Negi
 

What's hot (20)

Developing with the Modern App Stack: MEAN and MERN (with Angular2 and ReactJS)
Developing with the Modern App Stack: MEAN and MERN (with Angular2 and ReactJS)Developing with the Modern App Stack: MEAN and MERN (with Angular2 and ReactJS)
Developing with the Modern App Stack: MEAN and MERN (with Angular2 and ReactJS)
 
VDI Cost benefit analysis
VDI Cost benefit analysisVDI Cost benefit analysis
VDI Cost benefit analysis
 
Business Transactions with AppDynamics
Business Transactions with AppDynamicsBusiness Transactions with AppDynamics
Business Transactions with AppDynamics
 
CSRF Basics
CSRF BasicsCSRF Basics
CSRF Basics
 
Software Requirements Specification on Pubg Gaming App (SRS on PUBG)
Software Requirements Specification on Pubg Gaming App (SRS on PUBG)Software Requirements Specification on Pubg Gaming App (SRS on PUBG)
Software Requirements Specification on Pubg Gaming App (SRS on PUBG)
 
Event Driven Software Architecture Pattern
Event Driven Software Architecture PatternEvent Driven Software Architecture Pattern
Event Driven Software Architecture Pattern
 
Sql injection
Sql injectionSql injection
Sql injection
 
Active Directory Single Sign-On with IBM
Active Directory Single Sign-On with IBMActive Directory Single Sign-On with IBM
Active Directory Single Sign-On with IBM
 
Security Exploit of Business Logic Flaws, Business Logic Attacks
Security Exploit of Business Logic Flaws, Business Logic AttacksSecurity Exploit of Business Logic Flaws, Business Logic Attacks
Security Exploit of Business Logic Flaws, Business Logic Attacks
 
Rest API Security
Rest API SecurityRest API Security
Rest API Security
 
Cross Site Request Forgery Vulnerabilities
Cross Site Request Forgery VulnerabilitiesCross Site Request Forgery Vulnerabilities
Cross Site Request Forgery Vulnerabilities
 
LoadRunner Performance Testing
LoadRunner Performance TestingLoadRunner Performance Testing
LoadRunner Performance Testing
 
The importance of normalizing your security data to ECS
The importance of normalizing your security data to ECSThe importance of normalizing your security data to ECS
The importance of normalizing your security data to ECS
 
security misconfigurations
security misconfigurationssecurity misconfigurations
security misconfigurations
 
WebLogic authentication debugging
WebLogic authentication debuggingWebLogic authentication debugging
WebLogic authentication debugging
 
Use case-diagrams
Use case-diagramsUse case-diagrams
Use case-diagrams
 
Top 10 Web Application vulnerabilities
Top 10 Web Application vulnerabilitiesTop 10 Web Application vulnerabilities
Top 10 Web Application vulnerabilities
 
12 Steps to API Load Testing with Apache JMeter
12 Steps to API Load Testing with Apache JMeter12 Steps to API Load Testing with Apache JMeter
12 Steps to API Load Testing with Apache JMeter
 
Enable Authentication and Authorization with Azure Active Directory and Sprin...
Enable Authentication and Authorization with Azure Active Directory and Sprin...Enable Authentication and Authorization with Azure Active Directory and Sprin...
Enable Authentication and Authorization with Azure Active Directory and Sprin...
 
Logical Attacks(Vulnerability Research)
Logical Attacks(Vulnerability Research)Logical Attacks(Vulnerability Research)
Logical Attacks(Vulnerability Research)
 

Viewers also liked

Unit Testing Express and Koa Middleware in ES2015
Unit Testing Express and Koa Middleware in ES2015Unit Testing Express and Koa Middleware in ES2015
Unit Testing Express and Koa Middleware in ES2015Morris Singer
 
Cracking the Coding Interview code chix - oct 2012
Cracking the Coding Interview   code chix - oct 2012Cracking the Coding Interview   code chix - oct 2012
Cracking the Coding Interview code chix - oct 2012careercup
 
An introduction to Node.js
An introduction to Node.jsAn introduction to Node.js
An introduction to Node.jsKasey McCurdy
 
Dependency Inversion in large-scale TypeScript applications with InversifyJS
Dependency Inversion in large-scale TypeScript applications with InversifyJSDependency Inversion in large-scale TypeScript applications with InversifyJS
Dependency Inversion in large-scale TypeScript applications with InversifyJSRemo Jansen
 
Apache storm vs. Spark Streaming
Apache storm vs. Spark StreamingApache storm vs. Spark Streaming
Apache storm vs. Spark StreamingP. Taylor Goetz
 

Viewers also liked (6)

Unit Testing Express and Koa Middleware in ES2015
Unit Testing Express and Koa Middleware in ES2015Unit Testing Express and Koa Middleware in ES2015
Unit Testing Express and Koa Middleware in ES2015
 
Cracking the Coding Interview code chix - oct 2012
Cracking the Coding Interview   code chix - oct 2012Cracking the Coding Interview   code chix - oct 2012
Cracking the Coding Interview code chix - oct 2012
 
An introduction to Node.js
An introduction to Node.jsAn introduction to Node.js
An introduction to Node.js
 
Dependency Inversion in large-scale TypeScript applications with InversifyJS
Dependency Inversion in large-scale TypeScript applications with InversifyJSDependency Inversion in large-scale TypeScript applications with InversifyJS
Dependency Inversion in large-scale TypeScript applications with InversifyJS
 
Full Stack Unit Testing
Full Stack Unit TestingFull Stack Unit Testing
Full Stack Unit Testing
 
Apache storm vs. Spark Streaming
Apache storm vs. Spark StreamingApache storm vs. Spark Streaming
Apache storm vs. Spark Streaming
 

Similar to Unit Testing Express Middleware with Mocha and Promises

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 MizrahiRan Mizrahi
 
Writing robust Node.js applications
Writing robust Node.js applicationsWriting robust Node.js applications
Writing robust Node.js applicationsTom Croucher
 
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 .NETGianluca Carucci
 
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 Jeetendra singh
 
Good karma: UX Patterns and Unit Testing in Angular with Karma
Good karma: UX Patterns and Unit Testing in Angular with KarmaGood karma: UX Patterns and Unit Testing in Angular with Karma
Good karma: UX Patterns and Unit Testing in Angular with KarmaExoLeaders.com
 
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 frameworkBen Lin
 
Real World Lessons on the Pain Points of Node.JS Application
Real World Lessons on the Pain Points of Node.JS ApplicationReal World Lessons on the Pain Points of Node.JS Application
Real World Lessons on the Pain Points of Node.JS ApplicationBen Hall
 
Intro to Asynchronous Javascript
Intro to Asynchronous JavascriptIntro to Asynchronous Javascript
Intro to Asynchronous JavascriptGarrett Welson
 
Automated acceptance test
Automated acceptance testAutomated acceptance test
Automated acceptance testBryan Liu
 
Bonnes pratiques de développement avec Node js
Bonnes pratiques de développement avec Node jsBonnes pratiques de développement avec Node js
Bonnes pratiques de développement avec Node jsFrancois Zaninotto
 
How to perform debounce in react
How to perform debounce in reactHow to perform debounce in react
How to perform debounce in reactBOSC Tech Labs
 
Server side JavaScript: going all the way
Server side JavaScript: going all the wayServer side JavaScript: going all the way
Server side JavaScript: going all the wayOleg Podsechin
 
EWD 3 Training Course Part 38: Building a React.js application with QEWD, Part 2
EWD 3 Training Course Part 38: Building a React.js application with QEWD, Part 2EWD 3 Training Course Part 38: Building a React.js application with QEWD, Part 2
EWD 3 Training Course Part 38: Building a React.js application with QEWD, Part 2Rob Tweed
 
Building Web Apps with Express
Building Web Apps with ExpressBuilding Web Apps with Express
Building Web Apps with ExpressAaron Stannard
 
Nodejs first class
Nodejs first classNodejs first class
Nodejs first classFin Chen
 
MongoDB World 2019: Life In Stitch-es
MongoDB World 2019: Life In Stitch-esMongoDB World 2019: Life In Stitch-es
MongoDB World 2019: Life In Stitch-esMongoDB
 

Similar to Unit Testing Express Middleware with Mocha and Promises (20)

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
 
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
 
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
 
Good karma: UX Patterns and Unit Testing in Angular with Karma
Good karma: UX Patterns and Unit Testing in Angular with KarmaGood karma: UX Patterns and Unit Testing in Angular with Karma
Good karma: UX Patterns and Unit Testing in Angular with Karma
 
JS everywhere 2011
JS everywhere 2011JS everywhere 2011
JS everywhere 2011
 
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
 
Intro to ReactJS
Intro to ReactJSIntro to ReactJS
Intro to ReactJS
 
Real World Lessons on the Pain Points of Node.JS Application
Real World Lessons on the Pain Points of Node.JS ApplicationReal World Lessons on the Pain Points of Node.JS Application
Real World Lessons on the Pain Points of Node.JS Application
 
Intro to Asynchronous Javascript
Intro to Asynchronous JavascriptIntro to Asynchronous Javascript
Intro to Asynchronous Javascript
 
Automated acceptance test
Automated acceptance testAutomated acceptance test
Automated acceptance test
 
Tdd iPhone For Dummies
Tdd iPhone For DummiesTdd iPhone For Dummies
Tdd iPhone For Dummies
 
Bonnes pratiques de développement avec Node js
Bonnes pratiques de développement avec Node jsBonnes pratiques de développement avec Node js
Bonnes pratiques de développement avec Node js
 
How to perform debounce in react
How to perform debounce in reactHow to perform debounce in react
How to perform debounce in react
 
Server side JavaScript: going all the way
Server side JavaScript: going all the wayServer side JavaScript: going all the way
Server side JavaScript: going all the way
 
EWD 3 Training Course Part 38: Building a React.js application with QEWD, Part 2
EWD 3 Training Course Part 38: Building a React.js application with QEWD, Part 2EWD 3 Training Course Part 38: Building a React.js application with QEWD, Part 2
EWD 3 Training Course Part 38: Building a React.js application with QEWD, Part 2
 
Building Web Apps with Express
Building Web Apps with ExpressBuilding Web Apps with Express
Building Web Apps with Express
 
Intro to Sail.js
Intro to Sail.jsIntro to Sail.js
Intro to Sail.js
 
Nodejs first class
Nodejs first classNodejs first class
Nodejs first class
 
MongoDB World 2019: Life In Stitch-es
MongoDB World 2019: Life In Stitch-esMongoDB World 2019: Life In Stitch-es
MongoDB World 2019: Life In Stitch-es
 

Recently uploaded

Understanding Flamingo - DeepMind's VLM Architecture
Understanding Flamingo - DeepMind's VLM ArchitectureUnderstanding Flamingo - DeepMind's VLM Architecture
Understanding Flamingo - DeepMind's VLM Architecturerahul_net
 
Introduction Computer Science - Software Design.pdf
Introduction Computer Science - Software Design.pdfIntroduction Computer Science - Software Design.pdf
Introduction Computer Science - Software Design.pdfFerryKemperman
 
Open Source Summit NA 2024: Open Source Cloud Costs - OpenCost's Impact on En...
Open Source Summit NA 2024: Open Source Cloud Costs - OpenCost's Impact on En...Open Source Summit NA 2024: Open Source Cloud Costs - OpenCost's Impact on En...
Open Source Summit NA 2024: Open Source Cloud Costs - OpenCost's Impact on En...Matt Ray
 
Cloud Data Center Network Construction - IEEE
Cloud Data Center Network Construction - IEEECloud Data Center Network Construction - IEEE
Cloud Data Center Network Construction - IEEEVICTOR MAESTRE RAMIREZ
 
Simplifying Microservices & Apps - The art of effortless development - Meetup...
Simplifying Microservices & Apps - The art of effortless development - Meetup...Simplifying Microservices & Apps - The art of effortless development - Meetup...
Simplifying Microservices & Apps - The art of effortless development - Meetup...Rob Geurden
 
Call Us🔝>༒+91-9711147426⇛Call In girls karol bagh (Delhi)
Call Us🔝>༒+91-9711147426⇛Call In girls karol bagh (Delhi)Call Us🔝>༒+91-9711147426⇛Call In girls karol bagh (Delhi)
Call Us🔝>༒+91-9711147426⇛Call In girls karol bagh (Delhi)jennyeacort
 
Sending Calendar Invites on SES and Calendarsnack.pdf
Sending Calendar Invites on SES and Calendarsnack.pdfSending Calendar Invites on SES and Calendarsnack.pdf
Sending Calendar Invites on SES and Calendarsnack.pdf31events.com
 
Precise and Complete Requirements? An Elusive Goal
Precise and Complete Requirements? An Elusive GoalPrecise and Complete Requirements? An Elusive Goal
Precise and Complete Requirements? An Elusive GoalLionel Briand
 
英国UN学位证,北安普顿大学毕业证书1:1制作
英国UN学位证,北安普顿大学毕业证书1:1制作英国UN学位证,北安普顿大学毕业证书1:1制作
英国UN学位证,北安普顿大学毕业证书1:1制作qr0udbr0
 
Powering Real-Time Decisions with Continuous Data Streams
Powering Real-Time Decisions with Continuous Data StreamsPowering Real-Time Decisions with Continuous Data Streams
Powering Real-Time Decisions with Continuous Data StreamsSafe Software
 
SuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte Germany
SuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte GermanySuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte Germany
SuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte GermanyChristoph Pohl
 
VK Business Profile - provides IT solutions and Web Development
VK Business Profile - provides IT solutions and Web DevelopmentVK Business Profile - provides IT solutions and Web Development
VK Business Profile - provides IT solutions and Web Developmentvyaparkranti
 
Large Language Models for Test Case Evolution and Repair
Large Language Models for Test Case Evolution and RepairLarge Language Models for Test Case Evolution and Repair
Large Language Models for Test Case Evolution and RepairLionel Briand
 
How to submit a standout Adobe Champion Application
How to submit a standout Adobe Champion ApplicationHow to submit a standout Adobe Champion Application
How to submit a standout Adobe Champion ApplicationBradBedford3
 
Software Project Health Check: Best Practices and Techniques for Your Product...
Software Project Health Check: Best Practices and Techniques for Your Product...Software Project Health Check: Best Practices and Techniques for Your Product...
Software Project Health Check: Best Practices and Techniques for Your Product...Velvetech LLC
 
Unveiling Design Patterns: A Visual Guide with UML Diagrams
Unveiling Design Patterns: A Visual Guide with UML DiagramsUnveiling Design Patterns: A Visual Guide with UML Diagrams
Unveiling Design Patterns: A Visual Guide with UML DiagramsAhmed Mohamed
 
UI5ers live - Custom Controls wrapping 3rd-party libs.pptx
UI5ers live - Custom Controls wrapping 3rd-party libs.pptxUI5ers live - Custom Controls wrapping 3rd-party libs.pptx
UI5ers live - Custom Controls wrapping 3rd-party libs.pptxAndreas Kunz
 
Maximizing Efficiency and Profitability with OnePlan’s Professional Service A...
Maximizing Efficiency and Profitability with OnePlan’s Professional Service A...Maximizing Efficiency and Profitability with OnePlan’s Professional Service A...
Maximizing Efficiency and Profitability with OnePlan’s Professional Service A...OnePlan Solutions
 
Unveiling the Future: Sylius 2.0 New Features
Unveiling the Future: Sylius 2.0 New FeaturesUnveiling the Future: Sylius 2.0 New Features
Unveiling the Future: Sylius 2.0 New FeaturesŁukasz Chruściel
 

Recently uploaded (20)

Understanding Flamingo - DeepMind's VLM Architecture
Understanding Flamingo - DeepMind's VLM ArchitectureUnderstanding Flamingo - DeepMind's VLM Architecture
Understanding Flamingo - DeepMind's VLM Architecture
 
Introduction Computer Science - Software Design.pdf
Introduction Computer Science - Software Design.pdfIntroduction Computer Science - Software Design.pdf
Introduction Computer Science - Software Design.pdf
 
Open Source Summit NA 2024: Open Source Cloud Costs - OpenCost's Impact on En...
Open Source Summit NA 2024: Open Source Cloud Costs - OpenCost's Impact on En...Open Source Summit NA 2024: Open Source Cloud Costs - OpenCost's Impact on En...
Open Source Summit NA 2024: Open Source Cloud Costs - OpenCost's Impact on En...
 
Cloud Data Center Network Construction - IEEE
Cloud Data Center Network Construction - IEEECloud Data Center Network Construction - IEEE
Cloud Data Center Network Construction - IEEE
 
Advantages of Odoo ERP 17 for Your Business
Advantages of Odoo ERP 17 for Your BusinessAdvantages of Odoo ERP 17 for Your Business
Advantages of Odoo ERP 17 for Your Business
 
Simplifying Microservices & Apps - The art of effortless development - Meetup...
Simplifying Microservices & Apps - The art of effortless development - Meetup...Simplifying Microservices & Apps - The art of effortless development - Meetup...
Simplifying Microservices & Apps - The art of effortless development - Meetup...
 
Call Us🔝>༒+91-9711147426⇛Call In girls karol bagh (Delhi)
Call Us🔝>༒+91-9711147426⇛Call In girls karol bagh (Delhi)Call Us🔝>༒+91-9711147426⇛Call In girls karol bagh (Delhi)
Call Us🔝>༒+91-9711147426⇛Call In girls karol bagh (Delhi)
 
Sending Calendar Invites on SES and Calendarsnack.pdf
Sending Calendar Invites on SES and Calendarsnack.pdfSending Calendar Invites on SES and Calendarsnack.pdf
Sending Calendar Invites on SES and Calendarsnack.pdf
 
Precise and Complete Requirements? An Elusive Goal
Precise and Complete Requirements? An Elusive GoalPrecise and Complete Requirements? An Elusive Goal
Precise and Complete Requirements? An Elusive Goal
 
英国UN学位证,北安普顿大学毕业证书1:1制作
英国UN学位证,北安普顿大学毕业证书1:1制作英国UN学位证,北安普顿大学毕业证书1:1制作
英国UN学位证,北安普顿大学毕业证书1:1制作
 
Powering Real-Time Decisions with Continuous Data Streams
Powering Real-Time Decisions with Continuous Data StreamsPowering Real-Time Decisions with Continuous Data Streams
Powering Real-Time Decisions with Continuous Data Streams
 
SuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte Germany
SuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte GermanySuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte Germany
SuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte Germany
 
VK Business Profile - provides IT solutions and Web Development
VK Business Profile - provides IT solutions and Web DevelopmentVK Business Profile - provides IT solutions and Web Development
VK Business Profile - provides IT solutions and Web Development
 
Large Language Models for Test Case Evolution and Repair
Large Language Models for Test Case Evolution and RepairLarge Language Models for Test Case Evolution and Repair
Large Language Models for Test Case Evolution and Repair
 
How to submit a standout Adobe Champion Application
How to submit a standout Adobe Champion ApplicationHow to submit a standout Adobe Champion Application
How to submit a standout Adobe Champion Application
 
Software Project Health Check: Best Practices and Techniques for Your Product...
Software Project Health Check: Best Practices and Techniques for Your Product...Software Project Health Check: Best Practices and Techniques for Your Product...
Software Project Health Check: Best Practices and Techniques for Your Product...
 
Unveiling Design Patterns: A Visual Guide with UML Diagrams
Unveiling Design Patterns: A Visual Guide with UML DiagramsUnveiling Design Patterns: A Visual Guide with UML Diagrams
Unveiling Design Patterns: A Visual Guide with UML Diagrams
 
UI5ers live - Custom Controls wrapping 3rd-party libs.pptx
UI5ers live - Custom Controls wrapping 3rd-party libs.pptxUI5ers live - Custom Controls wrapping 3rd-party libs.pptx
UI5ers live - Custom Controls wrapping 3rd-party libs.pptx
 
Maximizing Efficiency and Profitability with OnePlan’s Professional Service A...
Maximizing Efficiency and Profitability with OnePlan’s Professional Service A...Maximizing Efficiency and Profitability with OnePlan’s Professional Service A...
Maximizing Efficiency and Profitability with OnePlan’s Professional Service A...
 
Unveiling the Future: Sylius 2.0 New Features
Unveiling the Future: Sylius 2.0 New FeaturesUnveiling the Future: Sylius 2.0 New Features
Unveiling the Future: Sylius 2.0 New Features
 

Unit Testing Express Middleware with Mocha and Promises

  • 1. express + mocha UNIT TESTING EXPRESS MIDDLEWARE By Morris Singer This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.
  • 2. ABOUT ME • Senior Software Engineer Cengage Learning • Expertise: • Sencha Touch • Angular.js and Node.js • Cordova / PhoneGap • Ruby on Rails
  • 3. AGENDA • Define Express Middleware and why it isn’t just a fancy term for controllers or endpoints. • Review behavior-driven development principles for unit testing. • Argue why Express Middleware are behavioral units. • Summarize common challenges testing behavior in Express. • Review Promises with the Q.js library. • Learn and implement a pattern for Promise-based Express Middleware. • Build tests for common scenarios using Mocha, Chai, Chai as Promised, and Mockgoose. • Answer questions. (10 minutes)
  • 4. EXPRESS MIDDLEWARE Building Your Product, One Layer at a Time
  • 5. A SIMPLE CASE One Middleware Per Endpoint app.get('hello.txt', function (req, res, next) { res.send(200, 'Hello World!'); }); “Why is it called ‘Middleware’ anyway?”
  • 6. MORE COMPLEX CASES Two Ways of Stacking Middleware app.get('hello.txt', function (req, res, next) { req.message = 'Hello World!'; next(); }, function (req, res, next) { res.send(200, req.message); } ); app.get('hello.txt', function (req, res, next) { req.message = 'Hello World!'; next(); }); ! app.get('hello.txt', function (req, res, next) { res.send(200, req.message); });
  • 7. THE MIDDLEWARE STACK app.get('hello.txt', function (req, res, next) { req.message = 'Hello World!'; next(); }, function (req, res, next) { res.send(200, req.message); } ); GET generateMessage sendMessage res.send
  • 9. MIDDLEWARE IS BEHAVIOR Middleware: • Define reusable components. • Create, modify, and store public variables. • Send responses to clients. • Comprise node packages.
  • 10. COMMON CHALLENGES Or, Why Back End Node Developers Often Avoid TDD
  • 11. HTTP RESPONSE TESTS it('should return a 500 error', function (done){ request({ method: 'POST', url: 'http://localhost:3000/api/endpoint' }, function (error, response, body){ expect(response.statusCode).to.equal(500); done(); }); }); What happens when we add a middleware to the stack?
  • 12. TESTING MID-STACK app.get('hello.txt', function (req, res, next) { req.message = 'Hello World!'; next(); }); ! app.get('hello.txt', function (req, res, next) { res.send(200, req.message); }); How do we pull out these anonymous functions?
  • 13. ILLUMINATING TEST FAILURES var httpMocks = require('node-mocks-http'); ! it('should call next()', function (done){ var req = httpMocks.createRequest(), res = httpMocks.createResponse(); ! middleware(req, res, function () { done(); }); }); What happens if next() is not called?
  • 14. KNOWING WHEN TO TEST var httpMocks = require('node-mocks-http'); ! it('should call next()', function (done){ var req = httpMocks.createRequest(), res = httpMocks.createResponse(); ! middleware(req, res); ! expect(req.foo).to.equal('bar'); }); When is the assertion run?
  • 15. TESTING WITH DATA app.get('path/to/post', function (req, res, next) { Post.findOne(params).exec(function (err, post){ res.json(200, post); }); }); Where do data come from?
  • 16. DEALING WITH POLLUTION it('should update the first post', function (){ /* ... */ }); ! it('should get the first post', function (){ /* ... */ }); How does one reset the data?
  • 17. MOCKING DEPENDENCIES app.get('endpoint', function (req, res, next) { request({ method: 'GET', url: 'http://example.com/api/call' }, function (error, response, body) { req.externalData = body; next(); }); }); How does one cut out the external data source?
  • 18. WRITING ROBUST TESTS app.get('hello.txt', function (req, res, next) { req.message = 'Hello World!'; next(); }); ! app.get('hello.txt', function (req, res, next) { res.send(200, req.message); }); What if someone adds a middleware?
  • 19. PROMISES Links in a Chain of Async Operations
  • 20. PYRAMID OF DOOM queryDatabase(params, function (result) { makeRequestOfThirdPartyService(result, function (result) { writeFile(result, function (handle) { sendFileOverHttp(handle, function (result) { }, function (err) { // Handle Error }); }, function (err) { // Handle Error }); }, function (err) { // Handle Error }); });
  • 21. PROMISES TO THE RESCUE queryDatabase() .then(makeRequestOfThirdPartyService) .then(updateDatabase) .then(writeFile) .then(sendFileOverHttp) .catch(function (err) { // Handle Errors }).done();
  • 22. WHAT IS A PROMISE A promise is: • a delegate • for an asynchronous action • that: • collects references to callbacks • maintains state, and • provides a mechanism for chaining.
  • 23. THEN, CATCH, FINALLY, DONE myPromise() .then(function (result) { ! }) .catch(function (err) { ! }) .finally(function () { ! }) .done(); Data sent, received, read, written, etc. Problems No matter what
  • 24. THE FLIP SIDE var Q = require('q'); ! function myPromise() { var deferred = Q.defer(); ! if (conditionX) { ! deferred.resolve('Result'); ! } else { ! deferred.reject(new Error()); ! } ! return deferred.promise; } Triggers then(). Passes ‘Result’ Triggers catch(). Passes new Error()
  • 25. PUTTING IT ALL TOGETHER var Q = require('q'); ! function myPromise() { var deferred = Q.defer(); ! if (conditionX) { ! ! } else { ! ! } ! return deferred.promise; } myPromise() ! .then(function (result) { ! }) ! .catch(function (err) { ! }) ! .finally(function () { ! }) ! .done(); deferred.resolve(‘Result’); deferred.reject(new Error());
  • 26. THE LIFE OF A PROMISE Pending Fulfilled then() finally() Rejected catch() finally()
  • 27. THE PROMISE CHAIN Start a new promise chain Continue the chain End the chain .then() .catch() .finally() Q.defer().promise Q.when() Q.promise() Q.fcall() .done() Return a promise
  • 28. IN PRACTICE Promise B Promise D Promise F Q.promise() .then().then().then().catch().finally().done() Promise A Promise C Promise E
  • 29. DO NOT BREAK YOUR CHAINS Otherwise, your user may be left hanging…
  • 30. NOT BREAKING CHAINS var Q = require('q'); ! Q.when(function () {}) .then(function (result) { var deferred = Q.defer(); ! /* Do async and call deferred.resolve() and deferred.reject(). */ ! return deferred.promise; }) .then(function (result) { var deferred = Q.defer(); ! /* Do async and call deferred.resolve() and deferred.reject(). */ ! return deferred.promise; }) .catch(function (err) { ! }) .done(); Resolving here calls the referenced function, passing the result as an argument. Rejections of either promise result in the referenced function called with err and uncaught rejections are thrown as errors here.
  • 31. Always return a promise or call done(). Period.
  • 32. EXPRESS + Q The “Eureka” Moment
  • 33. OVERVIEW • Pull middleware into endpoints and tests. • Mock req and res. • Use promises as link between middleware and endpoints. • Return client-server interaction to endpoint. • Use promises with Mocha.
  • 34. PULL MIDDLEWARE INTO ENDPOINTS, TESTS Endpoint Middleware Middleware Test Endpoint Middleware Middleware Test Test ! ! Old Paradigm " ! New Paradigm
  • 35. PULL MIDDLEWARE INTO ENDPOINTS, TESTS app.get('example/uri', function (req, res, next) { /* Middleware implementation */ }, function (req, res, next) { /* Middleware implementation */ }); var middleware = { first: function (req, res, next) {}, second: function (req, res, next) {} }; app.get('example/uri', middleware.first, middleware.second); ! ! Old Paradigm " ! New Paradigm
  • 36. MOCK REQ, RES • We need a way to call our middleware functions directly. • Our middleware functions expect req and res to be passed as arguments. • So, we mock req and res. module npm node-mocks-http https://www.npmjs.org/ package/node-mocks-http express-mocks-http https://www.npmjs.org/ package/express-mocks-http
  • 37. MOCK REQ, RES it ('should do something', function (done) { var requestParams = { uri: 'http://path.to/endpoint', method: 'POST' }; ! request(requestParams, function (error, response, body) { expect(response.body).to.equal( /* Expected Data */ ); done(); }); }); it ('resolves under condition X with result Y', function () { ! var req = httpMocks.createRequest(), res = httpMocks.createResponse(); ! /* Call middleware(req, res) and assert. */ ! }); ! ! Old Paradigm " ! New Paradigm
  • 38. USE PROMISES AS LINK BETWEEN MIDDLEWARE AND ENDPOINTS • Clean, standardized interface between asynchronous middleware and endpoints. • Both endpoints and tests can leverage the same mechanism in the middleware for serializing logic. then
  • 39. USE PROMISES AS LINK BETWEEN MIDDLEWARE AND ENDPOINTS module.exports = function (req, res, next) { ! /* Define middleware behavior and call res.json(), next(), etc. */ }; var Q = require('q'); module.exports = function (req, res) { var deferred = Q.defer(); /* Define middleware behavior and resolve or reject promise. */ return deferred.promise; }; ! ! Old Paradigm " ! New Paradigm
  • 40. RETURN CLIENT-SERVER INTERACTION TO ENDPOINT Endpoint Req Res Middleware Req Res Client Endpoint Req Res Middleware Req Res Client ! ! Old Paradigm " ! New Paradigm
  • 41. RETURN CLIENT-SERVER INTERACTION TO ENDPOINT var middleware = { first: function (req, res, next) {}, second: function (req, res, next) {} }; app.get('example/uri', middleware.first, middleware.second); var middleware = require('./middleware.js'); app.get('example/uri', function (req, res, next) { middleware.first(req, res) .then(function () { next(); }) .catch(res.json) .done(); }, middleware.second(req, res) .then(function () { next(); }) .catch(res.json) .done(); }); ! ! Old Paradigm " ! New Paradigm
  • 42. USING PROMISES WITH MOCHA (CHAI-AS-PROMISED) We need: • A test framework syntax that facilitates easy async testing. mocha (Supported natively in Mocha since 1.18.0) • An assertion syntax that we are familiar with. (Chai) • A set of assertions that facilitate easily writing tests of promises. (Chai-As-Promised) then
  • 43. USING PROMISES WITH MOCHA (CHAI-AS-PROMISED) describe('middleware', function () { it ('resolves under condition X with result Y', function () { ! var req = httpMocks.createRequest(), res = httpMocks.createResponse(); ! middleware(req, res).then(function (done) { /* Assert here. */ }).finally(done).done(); ! }); ! ! Old Paradigm " ! New Paradigm describe('middleware', function () { it ('resolves under condition X with result Y', function () { ! var req = httpMocks.createRequest(), res = httpMocks.createResponse(); ! return expect(middleware(req, res)).to.eventually.equal('value'); ! });
  • 44. THE NEW PARADIGM Looking at the Whole Picture
  • 45. ENDPOINTS Pull Middleware into Endpoint Return Client-Server Interaction to Endpoints var middleware = require('./middleware.js'); app.get('example/uri', function (req, res, next) { middleware(req, res) .then(function () { next(); }) .catch(res.json) .done(); });
  • 46. MIDDLEWARE Use Promise as Link Between Middleware and Endpoints var Q = require('q'); module.exports = function (req, res) { var deferred = Q.defer(); /* Define middleware behavior and resolve or reject promise. */ return deferred.promise; };
  • 47. Pull Middleware Into Tests TEST Use Promises with Mocha var httpMocks = require('node-mocks-http'), chai = require('chai'), chaiAsPromised = require('chai-as-promised'); chai.use(chaiAsPromised); Mock Req, Res var middleware = require('path/to/middleware'); var req, res; beforeEach(function (done) { req = httpMocks.createRequest(), res = httpMocks.createResponse(); }); describe('middleware', function () { it ('resolves under condition X with result Y', function () { return expect(middleware(req, res)).to.be.fulfilled.then(function () { /* Assert */ }); }); it ('rejects under condition X with error Y', function () { return expect(middleware(req, res)).to.be.rejectedWith('Error String'); }); });
  • 48. TESTING WITH DATA Mocking Mongoose and Using Fixtures to Build a Robust and Effective Test Suite
  • 49. THE PROBLEM WITH DATA We need a solution where: • Testing does not depend on the environment, • Data travel with the repo, • The database can easily be reset to an initial data set.
  • 50. (MONGODB + MONGOOSE)* * Solutions are available for other setups. You can also roll your own, without too much heartache.
  • 51. THE HIGH LEVEL • Mock MongoDB with in-memory database that can be reset between tests and thrown away after tests run. (Mockgoose) • Write test data as code that can move with the repo. (Fixtures) • Build test harness that loads fixtures into mock database before tests run.
  • 52. MOCKING MONGOOSE var mongoose = require('mongoose'); var mockgoose = require('mockgoose'); mockgoose(mongoose);
  • 53. CODING DATA IN FIXTURES module.exports.User = [ { name: 'Maeby' }, { name: 'George Michael' } ];
  • 54. LOADING FIXTURES var loader = require('pow-mongoose-fixtures'); ! var users = require('users.js'); /* User fixtures */ ! beforeEach(function (done) { loader.load(users); done(); }); ! /* Build Tests Here */
  • 55. TDD EXERCISES Use TDD in Pairs to Complete the Accompanying TDD / Express Exercises
  • 57. IMPROVEMENTS? • There are still some shortcomings in this approach, though it is better than other approaches I have seen. • Particularly, there are still some failure modes that will just timeout. • If you can improve on this pattern, PLEASE let me know!
  • 58. GET IN TOUCH # @morrissinger $ linkedin.com/in/morrissinger % morrissinger.com & github.com/morrissinger