Search code examples
node.jsajaxdojointernnock

Using nock with Intern4 and dojo - What is the correct approach


I have been experimenting with Intern as a testing platform for our code base which has a number of oddities to it. We basically load outside of the dojoLoader and core dojo files. This means we are outside the release process and loose all the goodness in terms of testing what that brings.

I have taken on the task of developing a tool chain that will manage the code (linting, build) and finally testing. And I have got to grips with most aspects of Unit testing and Functional Tests but testing 3rd Party APIs has really had me scratching my head.

On reading the docs I should be able to use Nock to mock the api, I have tried many different examples to get a basic hello world working and I have had varying degrees of success.

What I am noting is that Nock seems to play nicely when you are natively using node but it all falls to pieces once dojo is brought in to the equation. I have tried using axios, get, with tdd and bdd. All of which seem to fail miserably.

I have had a breakthrough moment with the below code, which will allow me to test a mock API with Nock successfully.

I have seen other examples taking a TDD approach to this but when I use the define pattern, there is no done() to signify the async process is complete.

While the below does work, I feel I have had to jump through many hoops to get to this point, i.e. lack of core node module util.promisify (I am currently running with Node V9.10.x). Lack of support for import et al, all which makes adopting examples very tough.

I am new to intern and I wonder if there a preferred or standard approach that I am missing which would make this simpler. I honestly prefer the TDD/BDD pattern visually but if the below is my only option for my setup then I will accept that.

define([
    'require',
    'dojo/node!nock',
    'dojo/node!http',
    'dojo/node!es6-promisify'
], function (require, nock, http, util) {
    const { registerSuite } = intern.getInterface('object');
    const { assert } = intern.getPlugin('chai');

    const { get } = http;
    const { promisify } = util;
    const _get = promisify(get);

    registerSuite('async demo', {
        'async test'() {
            const dfd = this.async();

            nock('http://some-made-up-service.com')
                .get('/request')
                .reply(200, {a:'Hello world!'});

            http.request({
                host:'some-made-up-service.com',
                path: '/request',
                port: 80
            }, function(res){
                res.on('data', dfd.callback((data) => {
                    var toJSON = JSON.parse(data.toString())
                    assert.strictEqual(toJSON.a, 'Hello world!');
                }))
            }).end();
        }
    });
});

My config is here also, I am sure there are entries in the file which are unnecessary but I am just figuring out what works at the moment.

{
    "node": {
        "plugins": "node_modules/babel-register/lib/node.js"
    },
    "loader": {
        "script": "dojo",
        "options": {
            "packages": [
                { "name": "app", "location": "asset/js/app" },
                { "name": "tests", "location": "asset/js/tests" },
                { "name": "dojo", "location": "node_modules/dojo" },
                { "name": "dojox", "location": "node_modules/dojox" },
                { "name": "dijit", "location": "node_modules/dijit" }
            ],
            "map": {
                "plugin-babel": "node_modules/systemjs-plugin-babel/plugin-babel.js",
                "systemjs-babel-build": "node_modules/systemjs-plugin-babel/systemjs-babel-browser.js"
            },
            "transpiler": "plugin-babel"
        }
    },
    "filterErrorStack": false,
    "suites": [
        "./asset/js/common/sudo/tests/all.js"
    ],
    "environments": ["node", "chrome"],
    "coverage": "asset/js/common/sudo/dev/**/*.js"
}

Solution

  • If you're using dojo's request methods, you can use Dojo's request registry to setup mocking, which can be a bit easier to deal with than nock when working with Dojo. Overall, though, the process is going to be similar to what's in your example: mock a request, make a request, asynchronously resolve the test when the request completes and assertions have been made.

    Regarding util.promisify, that's present in Node v8+, so you should be able to use it in 9.10.

    Regarding tdd vs bdd, assuming you're referring to Intern test interfaces (although it sounds like you may be referring to something else?), they all support the same set of features. If you can do something with the "object" interface (registerSuite), you can also do it with the "tdd" (suite and test) and "bdd" (describe and it) interfaces.

    Regarding the lack of support for import and other language features, that's dependent on how the tests are written rather than a function of Intern. If tests need to run in the Dojo loader, they'll need to be AMD modules, which means no import. However, tests can be written in modern ES6 and run through the TypeScript compiler or babel and emitted as AMD modules. That adds a build step, but at least tests can be written in a more modern syntax.

    Note that no node-specific functionality (nock, promisify, etc.) will work in a browser.