├── .gitignore ├── Procfile ├── package.json ├── readme.md ├── server.js └── test ├── mocha.opts └── test.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | 13 | # Coverage directory used by tools like istanbul 14 | coverage 15 | 16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 17 | .grunt 18 | 19 | # node-waf configuration 20 | .lock-wscript 21 | 22 | # Compiled binary addons (http://nodejs.org/api/addons.html) 23 | build/Release 24 | 25 | # Dependency directory 26 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git- 27 | node_modules -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: node server.js -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-api-test-example", 3 | "main": "server.js", 4 | "description": "Node API Test Example", 5 | "dependencies": { 6 | "body-parser": "1.0.1", 7 | "express": "4.0" 8 | }, 9 | "engines": { 10 | "node": "0.10.33" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | node.js API Testing Example 2 | ========================== 3 | 4 | This is an example of a testing stack for a node.js api. The stack I've put together here includes [Mocha](http://mochajs.org/), [Chai](http://chaijs.com/), and [SuperTest](https://github.com/tj/supertest). Use these tests for reference when you are setting this stack up in your node project. 5 | 6 | Please see my complete blog post here: [http://developmentnow.com/2015/02/05/make-your-node-js-api-bulletproof-how-to-test-with-mocha-chai-and-supertest/](http://developmentnow.com/2015/02/05/make-your-node-js-api-bulletproof-how-to-test-with-mocha-chai-and-supertest/) 7 | 8 | I'm a developer at [DevelopmentNow](http://developmentnow.com) in Portland, OR. Check us out! 9 | -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | var express = require('express'), 2 | bodyParser = require('body-parser'), 3 | port = process.env.PORT || 3000, 4 | app = express(); 5 | 6 | app.use(express.static(__dirname + '/public')) 7 | 8 | app.use(bodyParser.urlencoded({ 9 | extended: true 10 | })); 11 | app.use(bodyParser.json()); 12 | 13 | var locationList = []; 14 | 15 | app.get('/users/1', function(req, res) { 16 | var user = { 17 | name: "Karen", 18 | email: "karen@example.com", 19 | phoneNumber: "5556667777", 20 | role: "admin" 21 | }; 22 | res.send(user); 23 | }); 24 | 25 | app.put('/users/1', function(req, res) { 26 | var user = { 27 | name: req.body.name, 28 | email: req.body.email, 29 | phoneNumber: req.body.phoneNumber, 30 | role: req.body.role 31 | }; 32 | res.send(user); 33 | }); 34 | 35 | app.post('/locations', function(req, res) { 36 | var location = { 37 | addressStreet: req.body.addressStreet, 38 | addressCity: req.body.addressCity, 39 | addressState: req.body.addressState, 40 | addressZip: req.body.addressZip, 41 | userId: req.body.userId 42 | }; 43 | locationList.push(location); 44 | res.send(location); 45 | }); 46 | 47 | app.get('/users/1/location', function(req, res) { 48 | res.send(locationList[0]); 49 | }); 50 | 51 | app.get('/users/2/location', function(req, res) { 52 | if (req.body.userId == 2) { 53 | res.send(locationList[0]); 54 | } else { 55 | res.send(401); 56 | } 57 | }); 58 | 59 | app.listen(port); 60 | console.log("Server loaded. The magic happens on port", port) 61 | -------------------------------------------------------------------------------- /test/mocha.opts: -------------------------------------------------------------------------------- 1 | --timeout 25000 2 | -------------------------------------------------------------------------------- /test/test.js: -------------------------------------------------------------------------------- 1 | var should = require('chai').should(), 2 | expect = require('chai').expect, 3 | supertest = require('supertest'), 4 | api = supertest('http://localhost:3000'); 5 | 6 | describe('User', function() { 7 | 8 | var location1; 9 | var location2; 10 | var location3; 11 | var locations = [location1, location2, location3]; 12 | 13 | before(function(done){ 14 | 15 | api.post('/locations') 16 | .set('Accept', 'application/x-www-form-urlencoded') 17 | .send({ 18 | addressStreet: "111 Main St", 19 | addressCity: "Portland", 20 | addressState: "OR", 21 | addressZip: "97209", 22 | userId: 1 23 | }) 24 | .expect('Content-Type', /json/) 25 | .expect(200) 26 | .end(function (err, res) { 27 | location1 = res.body; 28 | }); 29 | 30 | 31 | api.post('/locations') 32 | .set('Accept', 'application/x-www-form-urlencoded') 33 | .send({ 34 | addressStreet: "222 Main St", 35 | addressCity: "Portland", 36 | addressState: "OR", 37 | addressZip: "97209", 38 | userId: 2 39 | }) 40 | .expect('Content-Type', /json/) 41 | .expect(200) 42 | .end(function (err, res) { 43 | location2 = res.body; 44 | }); 45 | 46 | api.post('/locations') 47 | .set('Accept', 'application/x-www-form-urlencoded') 48 | .send({ 49 | addressStreet: "333 Main St", 50 | addressCity: "Portland", 51 | addressState: "OR", 52 | addressZip: "97209", 53 | userId: 3 54 | }) 55 | .expect('Content-Type', /json/) 56 | .expect(200) 57 | .end(function (err, res) { 58 | location3 = res.body; 59 | done(); 60 | }); 61 | }); 62 | 63 | it('should return a 200 response', function(done) { 64 | api.get('/users/1') 65 | .set('Accept', 'application/json') 66 | .expect(200,done); 67 | }); 68 | 69 | it('should be an object with keys and values', function(done) { 70 | api.get('/users/1') 71 | .set('Accept', 'application/json') 72 | .expect(200) 73 | .end(function(err, res) { 74 | expect(res.body).to.have.property("name"); 75 | expect(res.body.name).to.not.equal(null); 76 | expect(res.body).to.have.property("email"); 77 | expect(res.body.email).to.not.equal(null); 78 | expect(res.body).to.have.property("phoneNumber"); 79 | expect(res.body.phoneNumber).to.not.equal(null); 80 | expect(res.body).to.have.property("role"); 81 | expect(res.body.role).to.not.equal(null); 82 | done(); 83 | }); 84 | }); 85 | 86 | it('should have a 10 digit phone number', function(done) { 87 | api.get('/users/1') 88 | .set('Accept', 'application/json') 89 | .expect(200) 90 | .end(function(err, res) { 91 | expect(res.body.phoneNumber.length).to.equal(10); 92 | done(); 93 | }); 94 | }); 95 | 96 | it('should have the role of admin', function(done) { 97 | api.get('/users/1') 98 | .set('Accept', 'application/json') 99 | .expect(200) 100 | .end(function(err, res) { 101 | expect(res.body.role).to.equal("admin"); 102 | done(); 103 | }); 104 | }); 105 | 106 | it('should be updated with a new name', function(done) { 107 | api.put('/users/1') 108 | .set('Accept', 'application/x-www-form-urlencoded') 109 | .send({ 110 | name: "Kevin", 111 | email: "kevin@example.com", 112 | phoneNumber: "9998887777", 113 | role: "editor" 114 | }) 115 | .expect(200) 116 | .end(function(err, res) { 117 | expect(res.body.name).to.equal("Kevin"); 118 | expect(res.body.email).to.equal("kevin@example.com"); 119 | expect(res.body.phoneNumber).to.equal("9998887777"); 120 | expect(res.body.role).to.equal("editor"); 121 | done(); 122 | }); 123 | }); 124 | 125 | it('should access their own locations', function(done) { 126 | api.get('/users/1/location') 127 | .set('Accept', 'application/x-www-form-urlencoded') 128 | .send({ 129 | userId: 1 130 | }) 131 | .expect(200) 132 | .end(function(err, res) { 133 | expect(res.body.userId).to.equal(1); 134 | expect(res.body.addressCity).to.equal("Portland"); 135 | done(); 136 | }); 137 | }); 138 | 139 | 140 | it('should not be able to access other users locations', function(done) { 141 | api.get('/users/2/location') 142 | .set('Accept', 'application/x-www-form-urlencoded') 143 | .send({ 144 | userId: 1 145 | }) 146 | .expect(401) 147 | .end(function(err, res) { 148 | if (err) return done(err); 149 | expect(res.error.text).to.equal("Unauthorized"); 150 | done(); 151 | }); 152 | }); 153 | 154 | }); 155 | --------------------------------------------------------------------------------