Как заставить Jest ждать, пока весь асинхронный код завершит выполнение, прежде чем ожидать утверждения

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

Проблема заключается в том, что тест, похоже, выполняется до того, как выполняется асинхронный обратный вызов, что приведет к сбою моих тестов.

Во всяком случае, вокруг? Могу ли я как-то дождаться завершения вызова асинхронного кода?

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

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

class Parent extends component
{
     render ()
     {
         <div>
            <Child />
         </div>
     }
}
class Child extends component
{
     DoStuff()
     {
         aThingThatReturnsAPromise().then((result) => {
           Store.Result = result
         })
     }

    render()
    {
       DoStuff()
       return(<div>{Store.Result}</div>)


    }
}
function aThingThatReturnsAPromise()
{
     return new Promise(resolve =>{
          eternalService.doSomething(function callback(result) {
               resolve(result)
          }
    }

}

Когда я это делаю в своем тесте, он терпит неудачу, потому что он запускается до того, как обратный вызов будет запущен.

jest.mock('eternalService', () => {
    return jest.fn(() => {
        return { doSomething: jest.fn((cb) => cb('fakeReturnValue');
    });
});

describe('When rendering Parent', () => {
    var parent;

    beforeAll(() => {
        parent = mount(<Parent />)
    });

    it('should display Child with response of the service', () => {
        expect(parent.html()).toMatch('fakeReturnValue')
    });
});

Как проверить это? Я понимаю, что угловой разрешает это с помощью zonejs, есть ли эквивалентный подход в React?

Ответ 1

Вот фрагмент кода, который ожидает разрешения ожидающих выполнения обещаний:

const flushPromises = () => new Promise(setImmediate);

Ответ 2

Я не знаю ничего родного для React, чтобы выполнить то, что вы ищете.

Однако, я смог выполнить это в аналогичном коде, вызвав beforeAll() @done после завершения установки. См. Изменения в коде ниже:

let setupComplete;
jest.mock('eternalService', () => {
    return jest.fn(() => {
        return { doSomething: jest.fn((cb) => { cb('fakeReturnValue'); setupComplete(); }) };
});
.
.
.
    beforeAll(done => {
        parent = mount(<Parent />)
        setupComplete = done;
    });
});

Ответ 3

в то время как псевдокод может быть реорганизован, чтобы следовать жизненному циклу React (используя componentWillMount() componentDidMount(), было бы намного легче протестировать. Однако ниже мой непроверенный псевдокод относится к изменениям для ваших тестовых кодов, не стесняйтесь проверить его и обновите его. Надеюсь, это поможет вам!

describe('When rendering Parent', () => {
    it('should display Child with the response of the service', function(done) => {
        const parent = mount(<Parent />);
        expect(parent.html()).toMatch('fakeReturnValue');
        done();
    });
});

Ответ 4

Асинхронный характер JavaScript является основной причиной, по которой модульное тестирование JavaScript заняло столько времени, чтобы созреть. Jest предоставляет множество шаблонов для тестирования асинхронного кода, описанного здесь: http://jestjs.io/docs/en/asynchronous.html Я использую async/ожидание наиболее широко, так как это делает чрезвычайно читаемые тесты.

Но поскольку вы издеваетесь над асинхронным методом, нет необходимости в асинхронном коде. Увидеть ниже.

let parent
let eternalService = jest.fn(() => 'fakeReturnValue');

beforeEach(() => {
    parent = mount(<Parent />)
    parent.instance().externalService = externalService
})

describe('When rendering Parent', () => {
    it('should display Child with response of the service', () => {
        expect(parent.html()).toMatch('fakeReturnValue')
    });
});

Ответ 5

Я предлагаю вам экспортировать aThingThatReturnsAPromise() из своего модуля или файла, а затем импортировать его в тестовый пример.

Поскольку aThingThatReturnsAPromise() возвращает обещание, вы можете использовать асинхронные функции тестирования Jest. Жест будет ждать вашего обещания, и вы сможете сделать свои утверждения.

describe('When rendering Parent', () => {
    var parent;

    beforeAll(() => {
        parent = mount(<Parent />)
    });

    it('should display Child with response of the service', () => {
        expect.assertions(1);

        return aThingThatReturnsAPromise().then( () => {
            expect(parent.html()).toMatch('fakeReturnValue');
        });
    });
});

Для получения дополнительной информации прочитайте, как Jest обрабатывает тестовые примеры с помощью обещаний в Jest Docs здесь