-
Notifications
You must be signed in to change notification settings - Fork 4
Server Side Testing
JT edited this page Nov 26, 2016
·
6 revisions
- Jasmine is essencial for testing client-side Angular2 and we believe in continuity; no need to use a different framework for the server-side when we're using one already
Server-side testing consists of a spec and integration tests for the router and controller of the endpoint
- Router:
- Primary assumptions to test for:
- is the router being called.
- was the function that was called the correct one.
- Primary assumptions to test for:
- Controller:
- Primary assumptions to test for:
- does the controller function output match expectations
- Primary assumptions to test for:
/**
* GET /api/wonder -> index
* POST /api/wonder -> create
* GET /api/wonder/:id -> show
* PUT /api/wonder/:id -> update
* DELETE /api/wonder/:id -> destroy
*/
let express = require('express');
import * as controller from './wonder.controller';
let router = express.Router();
router.get('/', controller.index);
router.get('/:id', controller.show);
router.post('/', controller.create);
router.put('/:id', controller.update);
router.patch('/:id', controller.update);
router.delete('/:id', controller.destroy);
export {router as wonderRoutes};
import proxyquire = require('proxyquire');
let pq = proxyquire.noPreserveCache();
import sinon = require('sinon');
let wonderCtrlStub = {
index: 'wonderCtrl.index',
show: 'wonderCtrl.show',
create: 'wonderCtrl.create',
update: 'wonderCtrl.update',
destroy: 'wonderCtrl.destroy'
};
let wonderRouterStub = {
get: sinon.spy(),
put: sinon.spy(),
patch: sinon.spy(),
post: sinon.spy(),
delete: sinon.spy()
};
// require the index with our stubbed out modules
let wonderIndex = pq('./wonder.router.js', {
'express': {
Router: function() {
return wonderRouterStub;
}
},
'./wonder.controller': wonderCtrlStub
});
describe('Wonder API Router:', function() {
it('should return an express router instance', function() {
expect(wonderIndex.wonderRoutes).toEqual(wonderRouterStub);
});
describe('GET /api/wonders', function() {
it('should route to wonder.controller.index', function() {
expect(wonderRouterStub.get.withArgs('/', 'wonderCtrl.index').calledOnce)
.toBe(true);
});
});
describe('GET /api/wonders/:id', function() {
it('should route to wonder.controller.show', function() {
expect(wonderRouterStub.get.withArgs('/:id', 'wonderCtrl.show').calledOnce)
.toBe(true);
});
});
describe('POST /api/wonders', function() {
it('should route to wonder.controller.create', function() {
expect(wonderRouterStub.post.withArgs('/', 'wonderCtrl.create').calledOnce)
.toBe(true);
});
});
describe('PUT /api/wonders/:id', function() {
it('should route to wonder.controller.update', function() {
expect(wonderRouterStub.put.withArgs('/:id', 'wonderCtrl.update').calledOnce)
.toBe(true);
});
});
describe('PATCH /api/wonders/:id', function() {
it('should route to wonder.controller.update', function() {
expect(wonderRouterStub.patch.withArgs('/:id', 'wonderCtrl.update').calledOnce)
.toBe(true);
});
});
describe('DELETE /api/wonders/:id', function() {
it('should route to wonder.controller.destroy', function() {
expect(wonderRouterStub.delete.withArgs('/:id', 'wonderCtrl.destroy').calledOnce)
.toBe(true);
});
});
});
import * as _ from 'lodash';
import Wonder from './wonder.model';
// if the wonder object was not found
function handleEntityNotFound(res) {
return function(entity) {
if (!entity) {
res.status(404).end();
return null;
}
return entity;
};
}
// if there was an error of any kind return approapriate status code
function handleError(res, statusCode = null) {
statusCode = statusCode || 500;
return function(err) {
res.status(statusCode).send(err);
};
}
function rndInt(min, max) {
return Math.floor(Math.random() * (max - min + 1) + min);
}
// When we update a wonder we are actually replacing an existing wonder
function updateWonder(res, wonder) {
return function(entity) {
if (entity) {
// wonder = old wonder
entity.name = wonder.name;
entity.created = new Date().toISOString();
// find new indeger for the x and y coors
entity.xcoor = rndInt(5, 80);
entity.ycoor = rndInt(10, 70);
entity.save((err, wonder) => {
// if there's an error than send a 400 code with error message
if (err)
res.status(400).json(err.errors.name);
// send back 200 code with new wonder json
res.json(wonder);
});
}
return null;
};
}
// IF the response needs the wonder abject as well
function respondWithResult(res, statusCode = null) {
statusCode = statusCode || 200;
return function(entity) {
if (entity) {
res.status(statusCode).json(entity);
return null;
}
};
}
// save new wonder endpoint: not replace
function saveUpdates(updates) {
return function(entity) {
let updated = _.merge(entity, updates);
return updated.save()
.then(update => {
return update;
});
};
}
// remove wonder endpoint
function removeEntity(res) {
return function(entity) {
if (entity) {
return entity.remove()
.then(() => {
res.status(204).end();
});
}
};
}
// Gets a list of Wonders
export function index(req, res) {
return Wonder.find().exec()
.then(respondWithResult(res))
.catch(handleError(res));
}
// Gets a single Wonder from the DB
export function show(req, res) {
return Wonder.findById(req.params.id).exec()
.then(handleEntityNotFound(res))
.then(respondWithResult(res))
.catch(handleError(res));
}
// Creates a new Wonder in the DB
export function create(req, res) {
return Wonder.findOne({}).sort({ created: 1 }).exec()
.then(updateWonder(res, req.body))
.catch(handleError(res));
}
// Updates an existing Wonder in the DB
export function update(req, res) {
if (req.body._id) {
delete req.body._id;
}
return Wonder.findById(req.params.id).exec()
.then(handleEntityNotFound(res))
.then(saveUpdates(req.body))
.then(respondWithResult(res))
.catch(handleError(res));
}
// Deletes a Wonder from the DB
export function destroy(req, res) {
return Wonder.findById(req.params.id).exec()
.then(handleEntityNotFound(res))
.then(removeEntity(res))
.catch(handleError(res));
}
import app from '../../server';
import request = require('supertest');
let addr = app.get('address');
// Wonder endpoint Testing
describe('Wonder API:', function() {
let newWonder;
let wonders;
// constant wonder array for testing purposes
const inputs = [1, 43, 2, 35, 65, 36, 10, 57, 32, 45, 90, 79, 32];
// Testing the POST api endpoint
describe('POST /api/wonders', function() {
// For the sake of conserving space, test using for loops
for (let counter = 0; counter < inputs.length; counter++) {
(function (input) {
// set up beforeAll's for every possible wonder that will be inputed
// all will execute and POST necessary wonders to test
return beforeAll((done) => {
request(addr)
.post('/api/wonders')
.send({
name: 'wonder: ' + input
})
.expect(200)
.expect('Content-Type', /json/)
.end((err, res) => {
if (err) {
done.fail(err);
}
expect(res.body.name).toBe('wonder: ' + input);
done();
});
});
})(inputs[counter]);
}
// Simple true = true to start the wonder POSTs
it('should respond back each query with inputted wonder', () => {
expect(true).toBe(true);
});
});
// This is where the real testing begins
describe('GET /api/wonders', function() {
// beforeAll GET all wonders from the DB
beforeAll(function(done) {
request(addr)
.get('/api/wonders')
.expect(200)
.expect('Content-Type', /json/)
.end((err, res) => {
if (err) {
done.fail(err);
}
wonders = res.body;
done();
});
});
// This response should be an array
it('should respond with JSON array', function() {
expect(wonders).toEqual(jasmine.any(Array));
});
// Loop through the response array and expect the output to be the same as
// the POSTs above
it('wonders should equal the original input array', () => {
for (let i = 0; i < 10; i++) {
(function (input, counter) {
return expect(wonders[counter].name).toBe('wonder: ' + input);
})(inputs[i + 3], (i + 3) % 10);
}
});
});
});