Как я могу утверждать, что функция "ожидала" асинхронной функции с использованием should.js - PullRequest
0 голосов
/ 29 ноября 2018

У меня есть асинхронная функция f, которая вызывает другую асинхронную функцию g.Чтобы проверить, звонит ли f g, я пишу g с помощью sinon и утверждаю, что он был вызван с помощью should.js.

'use strict';

require('should-sinon');
const sinon = require('sinon');

class X {
    async f(n) {
        await this.g(n);
        // this.g(n); // I forget to insert `await`!
    }
    async g(n) {
        // Do something asynchronously
    }
}

describe('f', () => {
    it('should call g', async () => {
        const x = new X();
        sinon.stub(x, 'g').resolves();
        await x.f(10);
        x.g.should.be.calledWith(10);
    });
});

Но этот тест проходит, даже если я забыл использовать await при вызове g в f.

Один из способов отловить эту ошибку - заставить заглушку вернуть фиктивное обещание и проверить, вызван ли ее then.

it('should call g', async () => {
    const x = new X();
    const dummyPromise = {
        then: sinon.stub().yields()
    };
    sinon.stub(x, 'g').returns(dummyPromise);
    await x.f(10);
    x.g.should.be.calledWith(10);
    dummyPromise.then.should.be.called();
});

Но это немного надоедает.Есть ли удобные способы сделать это?

Ответы [ 2 ]

0 голосов
/ 03 декабря 2018

Вместо заглушки then лучше всего заглушить g таким образом, чтобы он устанавливал логическое значение на следующей итерации цикла событий.Затем вы можете проверить это логическое значение после вызова f, чтобы убедиться, что f его ожидало:

it('should call g', async () => {
    const x = new X();
    let gFinished = false;
    sinon.stub(x, 'g').callsFake(() => {
        return new Promise((resolve) => {
            setImmediate(() => {
                gFinished = true;
                resolve();
            });
        });
    });
    await x.f(10);
    x.g.should.be.calledWith(10);
    gFinished.should.be.true();
});

Редактировать: Конечно, это не идеальная гарантия, потому чтовы могли бы f ждать любого обещания, которое ожидает, по крайней мере, столько времени, сколько требуется для g разрешения.Примерно так:

async f(n) {
    this.g(n);
    await new Promise((resolve) => {
        setImmediate(() => {
            resolve();
        });
    });
}

Это может привести к тому, что тест, который я написал, будет пройден, хотя он все еще неверен.Так что на самом деле все сводится к тому, насколько строго вы пытаетесь быть со своими тестами.Вы хотите, чтобы было буквально невозможно иметь ложный положительный результат?Или это нормально, если какой-то очевидный обман может потенциально скинуть его?

В большинстве случаев я считаю, что с последним все в порядке, но на самом деле это зависит от вас и / или вашей команды.

0 голосов
/ 29 ноября 2018

Ваш пример для f показывает некорректный дизайн кода, который становится более очевидным, если вы пишете ту же функцию без синтаксиса async/await:

f(n) { return g(n).then(()=>{}); }

Это достигается тем же поведением -становится ли трудно определить g решено (при условии, что вы не знаете, вернул ли f обещание g, что равносильно тому, чтобы не знать, ожидал ли f g).Если f не заинтересован в результате g, он должен просто вернуть его, а не скрывать.Тогда вы можете просто проверить результат.

Если вы считаете, что f может потребоваться последовательно выполнить несколько вызовов async await, чтобы разрешить несколько g_1, g_2, ..., вы можете создать цепочку тестов.утверждая в заглушке g_n+1, что фиктивное обещание g_n было выполнено.В целом ваш подход к проверке фиктивного обещания на его статус в порядке.

...