Метод макета обещания не работает - PullRequest
1 голос
/ 30 апреля 2019

Попытка написать тесты в JEST для моего node.js, написанного на машинописи. Функция, которую я хочу протестировать, очень сложна (я имею в виду, что внутри происходит много всего). Он возвращает Promise , и внутри него есть 2 другие функции, которые возвращают Promises (одна из которых выполняет асинхронный вызов) + являются BehaviorSubject , Observables (созданный с forEach из массива) с forkJoin .

Я пытаюсь смоделировать ответ 1 вложенной функции Promise . Результат этого присваивается Observable . Причина, по которой я пытаюсь изменить ответ, заключается в том, что я хочу проверить свою основную функцию.

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

Вот пример (попробуйте упростить) моего модуля:

import { BehaviorSubject, forkJoin } from 'rxjs'

export const tableConfig: any[] = [{
    tableName: 'TableOne'
},
{
    tableName: 'TableTwo'
},
{
    tableName: 'TableThree'
}];

const exampleApiTableData: any = {
    TableOne: [],
    TableTwo: [],
    TableThree: [],
}

export const pullTableData = (tableName: string): Promise<any[]> => { //async Promise function 1
    return new Promise((resolve, reject) => {
        // Here is a async api call with some more logic but to make it simple and short giving such an example
        setTimeout(() => {
            resolve(exampleApiTableData[tableName]);
        }, 1000);
    })
}

export const buildNewTable = (tableOne: any[], tableTwo: any []): Promise<any[]> => { // Promise function 2
    return new Promise((resolve, reject) => {
        //simplified example
        resolve(tableOne.concat(tableTwo));
    })
}

export const getTables = (): Promise<any> => { // Master
    return new Promise((resolve, reject) => {
        const errors: string[] = [];
        const allTableData$: any[] = [];
        const observableNames: any = {};

        tableConfig.forEach(table => {
            observableNames[table.tableName + 'Source'] = new BehaviorSubject<string[]>([]);
            observableNames[table.tableName] = observableNames[table.tableName + 'Source'].asObservable();
            allTableData$.push(observableNames[table.tableName]);
            pullTableData(table.tableName).then((result: any[]) => {
                observableNames[table.tableName + 'Source'].next(result);
                observableNames[table.tableName + 'Source'].complete();
            }).catch((error: any) => {
                errors.push(error);
                observableNames[table.tableName + 'Source'].next(error);
                observableNames[table.tableName + 'Source'].complete();
            })
        });

        forkJoin(allTableData$).subscribe((results: any) => {
            if (errors.length > 0) reject(errors);
            buildNewTable(observableNames.TableOneSource.value, observableNames.TableTableTwoSource.value).then((result: any[]) => {
                // console.log(result);
                resolve(result);
            }).catch((error: any) => {
                // console.log(error);
                reject(error);
            });
        });
    });
}

Это тест, который я создал, но вместо получения значения mockRejectedValue он вызывает pullTableData все время

import * as tableMethods from './index'

describe(`Test the Table methods`, () => {
    test(`it should return and error`, () => {
        const expectedError = `I'm an error`
        jest.fn(tableMethods.pullTableData).mockRejectedValue(expectedError);

        return tableMethods.getTables().then((data: any) => {

        }).catch((error: any) => {
            expect(error).toBe(expectedError);
        })
    })
})

Что я делаю не так? Есть ли способ смоделировать pullTableData и buildNewTable , чтобы я мог проверить функцию getTable ?

1 Ответ

0 голосов
/ 30 апреля 2019

Вам просто нужно несколько изменений кода:

import { BehaviorSubject, forkJoin } from 'rxjs'
import * as index from './index';  // <= import module into itself

// ...    

export const getTables = (): Promise<any> => { // Master

  // ...

      index.pullTableData(table.tableName).then((result: any[]) => {  // <= call pullTableData using the module

  // ...

      if (errors.length > 0) reject(new Error(errors.join(', ')));  // <= reject using an Error object

  // ...
}

... тогда вы можете проверить это следующим образом:

import * as tableMethods from './index'

describe(`Test the Table methods`, () => {
  test(`it should return and error`, async () => {  // <= async test function
    const expectedError = `I'm an error`
    jest.spyOn(tableMethods, 'pullTableData').mockRejectedValue(expectedError);  // <= use jest.spyOn

    await expect(tableMethods.getTables()).rejects.toThrow(`I'm an error, I'm an error, I'm an error`);  // Success!
  })
})

Подробности

Перемешивание функции с использованием чего-то вроде jest.spyOn заменяет экспорт модуля для функции.

В этом случае getTables вызывал pullTableData напрямую, поэтомуПересмешивание экспорта модуля для pullTableData не имело никакого эффекта.

Модули TypeScript и ES6 автоматически поддерживают циклические зависимости , поэтому вы можете импортировать модуль в себя, чтобы вызвать экспорт модуля для функции, так что при экспорте модуля имитируется вызываемая функция вместо оригинала.

Рекомендуется всегда reject использовать объект Error.

Отклонение с объектом Error также позволяет использовать .rejects.toThrow в своем тесте.

Использование rejects и resolves требуетвы должны либо вернуть Promise из expect, либо использовать async тестовую функцию и awaitPromise.

...