基于承诺的方法的模拟不起作用

Mock of promise based method is not working

正在尝试用 JEST 为我用打字稿编写的 node.js 编写测试。我要测试的功能非常复杂(我的意思是里面发生了很多事情)。它 returns 一个 Promise 并且在其中还有 2 个其他函数正在返回一个 Promises (其中一个正在进行异步调用) + BehaviorSubjectObservables(使用 forEach 从数组创建)和 forkJoin .

我正在尝试模拟基于 Promise 的嵌套函数 1 的响应。它的结果被分配给一个 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);
        })
    })
})

我做错了什么? 有没有办法模拟 pullTableDatabuildNewTable 以便我可以测试 getTable 函数?

您只需更改几处代码:

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 模块 support cyclic dependencies automatically 因此您可以将模块导入到自身中,以便为函数调用 module export 以便在模拟模块导出时调用模拟函数而不是原始函数。

最佳做法是始终reject 使用 Error 对象。

使用 Error 对象拒绝还允许您在测试中使用 .rejects.toThrow

使用 rejects and resolves 要求您从 expect return Promise 或使用 async 测试函数和 await Promise.