简体   繁体   中英

Unit tests: Testing inner Promise

I've been searching the web for a while and I've yet to find the answer to this.

Let's say we're testing the following:

function p() {
    return Promise.resolve(1);
}

class Sample {
    constructor() {
        this.stuff = 2;
    }

    sample () {
        p().then((data) => {
            this.stuff = data;
        });

        //more stuff
    }
}

module.exports = {Sample};

How could we check that, eventually this.stuff is populated without returning said promise?

This is what I've been able to do until now:

let expect = require('chai').expect;
let sinon = require('sinon');

let Sample = require('./source.js').Sample;

describe('stuff', function () {
    it('test2', function() {
        let sample = new Sample();

        return Promise.resolve(sample.sample())
            .then(() => {
                expect(sample.stuff).to.be.eql(2);
            });
    });
});

In which, each then gets the next tick (if we had multiple then s in our sample we'd also need multiple then s in our test).

(And before you say process.nextTick , I want this to run in the browser with karma-mocha)

Thanks in advance!

This could be easily tested if the code was restructured, to support testability.

One way to do this would be to take an optional p interface for the object instantiation. The value could default to your defined p but would allow for a test to create a fake object and verify that your Sample operates on it correctly.

function p() {
    return Promise.resolve(1);
}

class Sample {
    constructor(pFn) {
        this.stuff = 2;
        this.p = pFn || p;
    }

    sample () {
        this.p().then((data) => {
            this.stuff = data;
        });

        //more stuff
    }
}

This would allow you to provide a promise-like object, for your unit test, that executes synchronously.

One way would be to periodically check for the set value.

it('test2', function(done) {
    let sample = new Sample();
    let assert = function() {
        if(sample.stuff === 2) {
            done();
        } else {
            setTimeout(assert, 0);
        }
    };

    sample.sample();
    assert();
});

It's not the prettiest, and won't give the best error message on a failure, but it would work. If sample.stuff isn't set to 2 within a couple seconds (I think 3 seconds is the default) then the test will fail because done() was not called in time.


Another possible option is to have the test replace stuff with a setter. Then you can expect() on set.

it('test2', function(done) {
    let sample = new Sample();
    Object.defineProperty(sample, 'stuff', { set: function (value) {
        expect(value).to.be.eql(2);
        done();
    }});

    sample.sample();
});

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM