Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Testing Node.js Applications

Testing Node.js Applications

A run through on how to effectively test your Node.js applications

Thanos Polychronakis

June 28, 2017
Tweet

More Decks by Thanos Polychronakis

Other Decks in Technology

Transcript

  1. Testing Node.js
    Applications

    View full-size slide

  2. Who is this presentation for?
    Early - Mid stage startups
    Personal Projects
    Boilerplates

    View full-size slide

  3. Today's Menu
    1. Testing tools & libraries
    2. How to setup your Node.js Application
    3. Testing directory structure and overview
    4. Walkthrough of setting up test cases

    View full-size slide

  4. Testing Tools & Libraries
    Mocha - test runner -
    Chai - BDD Assertion library -
    Sinon - Spies and Mocks -
    Faker - generate fake data - GH
    mochajs.org
    chaijs.org
    sinonjs.org
    Marak/Faker.js
    These are the tools I use and can talk about, tools
    can change, patterns remain the same...

    View full-size slide

  5. Setting up your Node.js App
    Avoid initializing anything outside of functions.
    Wrap your whole application in a single boot
    entry point.

    View full-size slide

  6. Setting up your Node.js App
    const postgreService = require('./services/postgre.service');
    const webService = require('./services/web.service');
    const app = module.exports = {};
    app.init = Promise.method(function () {
    return Promise.all([
    postgreService.init(),
    // ... any other low-level / datastore service
    ])
    .then(function () {
    return Promise.all([
    webService.init(),
    // ... other high-level services
    ]);
    });
    });
    // Determine if module was the execution entry point
    const isStandAlone = require.main === module;
    if (isStandAlone) {
    app.init();
    }
    index.js
    Application Boot
    Standalone Test
    Ignition

    View full-size slide

  7. Tests Directory Structure
    - /test/
    |-- asserts/
    |-- e2e/
    |-- fixtures/
    |-- lib/
    |-- unit/

    View full-size slide

  8. test/asserts/
    Perform standardized tests on your models
    Tests Directory Structure
    Test expected properties of Object
    Test expected types of Object
    Test expected values of Object

    View full-size slide

  9. test/asserts/
    Tests Directory Structure
    const chai = require('chai');
    const expect = chai.expect;
    const itemTests = module.exports = {};
    itemTests.runAll = function (item) {
    itemTests.testProperties(item);
    itemTests.testTypes(item);
    itemTests.testValues(item);
    };
    Part 1/2

    View full-size slide

  10. test/asserts/
    Tests Directory Structure
    itemTests.testProperties = function (item) {
    expect(item).to.be.an('object');
    expect(item).to.have.keys([
    'id',
    'name',
    ]);
    };
    itemTests.testTypes = function (item) {
    expect(item.id).to.be.a('string');
    expect(item.name).to.be.a('string');
    };
    itemTests.testValues = function (item) {
    expect(item.id.length).to.equal(12);
    expect(item.name).to.match(/^[\w]{3,8}$/);
    };
    Part 2/2

    View full-size slide

  11. test/fixtures/
    Tests Directory Structure
    Easy creation of input data.
    Use faker for randomizing data.
    Definitely not idempotent functions.

    View full-size slide

  12. test/fixtures/
    Tests Directory Structure
    test/fixtures/account.fix.js
    const faker = require('faker');
    const phoneFix = require('./phone-numbers.fix');
    const accountFix = module.exports = {};
    accountFix.minFields = () => ({
    name: faker.name.firstName(),
    phone_number: phoneFix.getUS(),
    });

    View full-size slide

  13. test/lib/
    Tests Directory Structure
    Contains master test boot "test.lib.js" (more next).
    Libraries to perform all e2e requests per model.
    As stand-alone methods.
    As Mocha Setup Cases.

    View full-size slide

  14. test/lib/test.lib.js
    Tests Directory Structure
    Is included by all tests.
    Has two master methods to boot for e2e and unit.
    e2e practically boots up your Node.js App.
    Unit boot prepares anything you need to have
    ready.
    Contains any commonly used helpers.

    View full-size slide

  15. test/lib/account.lib.js
    Tests Directory Structure
    Provide methods to create account[s] of all types (?).
    Provide methods to setup mocha with new accounts.

    View full-size slide

  16. test/lib/account.lib.js
    Tests Directory Structure
    const axios = require('axios');
    const accountFix = require('../fixtures/account.fix');
    const accountLib = module.exports = {};
    accountLib.setupOne = function() {
    beforeEach(function () {
    const accountData = accountFix.one();
    return accountLib.create(accountData)
    .then((accountRecord) => {
    this.accountOne = accountRecord;
    });
    });
    };
    accountLib.create = function (accountData) {
    return axios.post('/account/', accountData)
    .then(function (res) {
    return res.data;
    });
    }

    View full-size slide

  17. Putting it all
    together

    View full-size slide

  18. Test Account Creation
    Putting all together
    const expect = require('chai').expect;
    const testLib = require('../lib/test.lib');
    const accountFix = require('../fixtures/account.fix');
    const accountLib = require('../lib/account.lib');
    const accountAssert = require('../asserts/account.assert');
    describe('Account Create', function () {
    testLib.init();
    describe('Nominal behaviors', function() {
    it('Should create an account and get expected outcome', function () {
    const accountData = accountFix.one();
    return accountLib.create(accountData)
    .then((accountRecord) => {
    accountAssert.runAll(accountRecord);
    });
    });
    });
    });

    View full-size slide

  19. Test Account Creation
    Putting all together
    Important points:
    Wrap all your test cases in a single "describe"
    statement, so you can easily skip the whole suite.
    Wrap your tests in double "describe" statements, you
    will need it to better setup your cases (more on that
    later).

    View full-size slide

  20. Test Account Creation
    Putting all together
    const expect = require('chai').expect;
    const testLib = require('../lib/test.lib');
    const accountLib = require('../lib/account.lib');
    const eventsFix = require('../fix/events.fix');
    const eventsLib = require('../lib/events.lib');
    const eventsAssert = require('../asserts/events.assert');
    describe('Create Event', function () {
    testLib.init();
    describe('Nominal behaviors', function() {
    accountLib.setupOne();
    it.only('Should create an account and get expected outcome', function () {
    const eventData = eventFix.one();
    return eventLib.create(this.accountOne.id, eventData)
    .then((eventRecord) => {
    eventAssert.runAll(eventRecord);
    });
    });
    });
    });

    View full-size slide

  21. Test Account Creation
    Putting all together
    More points:
    Avoid inlining actual XHR calls in tests.
    Make your helper methods flexible.
    Reserve that only for very edge cases.
    Beware of context.

    View full-size slide

  22. To Summarise
    1. Invoke tester library INIT to boot service.
    2. Run all setup instructions to bring service to desired
    state.
    3. Run test case.
    4. Optionally run the outcome through your automated
    asserters.
    Flow of running tests

    View full-size slide

  23. Thank you
    Thanasis Polychronakis
    @thanpolas
    https://speakerdeck.com/thanpolas

    View full-size slide

  24. Questions?
    Thanasis Polychronakis
    @thanpolas
    https://speakerdeck.com/thanpolas

    View full-size slide