Как протестировать сценарий бесконечной рекурсивной рабочей функции javascript

#javascript #unit-testing #mocha.js #sinon

#javascript #модульное тестирование #mocha.js #sinon

Вопрос:

Я безуспешно пытаюсь протестировать следующий «рабочий» метод в классе, поскольку это «бесконечный цикл» / рекурсивный:

 Class TheClassYaDaYaDa {
    constructor(var1) {
        (...)
    }

    async worker() {
        return new Promise(async (resolve, reject) => {
            try {
                await this.doStuff();
                return resolve();
            } catch (err) {
                return reject(err);
            } finally {
                this.worker();
            }
        });
    }

}
 

И это тест, который я создаю:

 it('It should not throw error', async function () {
    let error = false;
    const var1 = 'parameterTest';
    stub1 = sinon.stub(TheClassYaDaYaDa.prototype, 'doStuff').resolves();

    // if I use the following stub logically the test will not succeed. If I don't I get an infinte loop
    // stub2 = sinon.stub(TheClassYaDaYaDa.prototype, 'worker').resolves();

    let classToTest;
    try {
        classToTest = new TheClassYaDaYaDa(limitedInstance);
        result = await classToTest.worker();
    } catch (err) {
        error = err;
    }
    expect(error).to.be.false;
    sinon.assert.calledOnce(stub1);
    
    //also assert that the finally statement run at least once!
});
 

Есть ли способ протестировать этот сценарий?

Ответ №1:

Я бы подошел к этому с другой стороны, рекурсия всегда сложна, но один из популярных проектов — разделить логику планирования и выполнения на две части.

Рассмотрим следующее:

 async worker() {
    await this.scheduleWorker();
}

private scheduleWorker() {
    // return new Promise ...
}
 

Теперь можно проверить, что функция scheduleWorker вызывает worker, а также что worker вызывает scheduleWorker (если доступны частные методы).

Если нет, вы можете издеваться над методом worker, чтобы возвращать рабочую логику только при первом вызове и noops при последующих вызовах. Это также будет работать для вашего текущего дизайна, позволяя вам утверждать, что рекурсивный вызов был правильно запущен.

Я не очень хорошо разбираюсь в sinon, но вы хотите что-то похожее на:

 var originalWorker = classToTest.originalWorker;
classToTest.originalWorker = function() {
    originalWorker();
    classToTest.originalWorker = function() { }
}
 

Большинство макетных фреймворков имеют аналогичные элементы управления для многократного включения методов макета (для последующих вызовов).