Skip to content

Instantly share code, notes, and snippets.

@luishrd
Created June 21, 2018 18:29
Show Gist options
  • Save luishrd/ea25b1e1c25a9e076c49aba924bcbf1c to your computer and use it in GitHub Desktop.
Save luishrd/ea25b1e1c25a9e076c49aba924bcbf1c to your computer and use it in GitHub Desktop.
CS10 - Server Testing

Server Testing Using Jest

Objectives

  • introduce today's project.
  • introduce the TDD/BDD workflow.
  • use TDD/BDD to implement a set of requirements.
  • configure jest's test environment to run in node mode.
  • introduce the supertest library.
  • use the cross-env module to configure the environment before running the tests.
  • write integration tests that hit a test database.

Requirements

  • when making a GET to the / endpoint the API should respond with status code 200 and the following JSON object: { api: 'running' }.
  • the User model should hash the password before saving it to the database.

TDD === Test Driven Development.

BDD === Behavior Driven Development (search for Dan North video)

TDD Philosophy (red - green - refactor)

  • no code is written unless a test requires it.
  • only write enough code to make the test pass.
  • once the test passes look for opportunities to clean up test and code.

System

  • features
    • user stories
      • scenarios

User Story Example

As a: sales executive. I want: login to the system. So that: I can see my sales for the day/quarter.

Scenarios

  • Given: a username

    • and: a password
  • When: the username is valid

    • and: the password is valid
  • Then: the system will allow login

    • and: the user is redirected to their landing page.
  • Given: a username

    • and: a password
  • When: the username is valid

    • and: the password is invalid
  • Then: the system will NOT allow login

    • and: the system will show a message to the user.
    • and: the user is redirected to their login page.

As a (role): payroll officer. I want: login to the system. So that: I can run the payroll.

By default Jest uses jsdom, for node we need to change that default.

{
"name": "servertesting",
"version": "1.0.0",
"description": "",
"main": "server.js",
"scripts": {
"test": "jest --watch --verbose"
},
"keywords": [],
"author": "",
"license": "ISC",
"jest": {
"testEnvironment": "node"
},
"dependencies": {
"bcrypt": "^2.0.1",
"express": "^4.16.3",
"mongoose": "^5.1.6"
},
"devDependencies": {
"cross-env": "^5.2.0",
"jest": "^23.1.0",
"supertest": "^3.1.0"
}
}
const express = require('express');
const server = express();
server.get('/', (req, res) => {
res.status(200).json({ api: 'running' });
});
module.exports = server;
/*
- when making a GET to the `/` endpoint
the API should respond with status code 200
and the following JSON object: `{ api: 'running' }`.
*/
const request = require('supertest');
const server = require('./server'); // this is our first red, it doesn't exist
describe('server.js', () => {
it('should return an OK status code and a JSON object fron the index route', async () => {
// it('should return an OK status code and a JSON object fron the index route', async () => {
const expectedStatusCode = 200;
const expectedBody = { api: 'running' };
// do a get request to our api (server.js) and inspect the response
const response = await request(server).get('/');
expect(response.status).toEqual(expectedStatusCode);
expect(response.body).toEqual(expectedBody);
expect(response.type).toEqual('application/json');
// create the user
let supertestResponse = await request(server).get('/123');
// check the user exist
supertestResponse = await request(server).delete('/123');
supertestResponse = await request(server).get('/123');
// check the user is not there anymore
// using .then()
// let response;
// return request(server).get('/').then(res => {
// response = res;
// expect(response.status).toEqual(expectedStatusCode);
// expect(response.body).toEqual(expectedBody);
// expect(response.type).toEqual('application/json');
// })
});
});
// ./users/User.js
const mongoose = require('mongoose');
const bcrypt = require('bcrypt');
const userSchema = new mongoose.Schema({
username: {
type: String,
unique: true,
},
password: String,
});
userSchema.pre('save', function() {
bcrypt
.hash(this.password, 10)
.then(hash => {
this.password = hash;
next();
})
.catch(err => {
console.log(err);
});
});
module.exports = mongoose.model('User', userSchema);
// ./users/User.spec.js
const mongoose = require('mongoose');
const User = require('./User');
describe('user model', () => {
beforeAll(() => {
return mongoose.connect('mongodb://localhost/testdb');
});
afterEach(() => {
return User.remove();
});
afterAll(() => {
return mongoose.disconnect();
});
it('should hash the password before saving the user', async () => {
const bilbo = { username: 'bilbo', password: 'baggins' };
const savedUser = await User.create(bilbo);
expect(savedUser.username).toEqual(bilbo.username);
expect(savedUser.password).not.toEqual(bilbo.password);
expect(savedUser.password).toHaveLength(60);
});
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment