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

时间:2019-04-30 03:56:23

标签: typescript unit-testing jestjs ts-jest

尝试用 JEST 为用打字稿编写的node.js编写测试。我要测试的功能非常复杂(我的意思是内部发生了很多事情)。它返回一个 Promise ,里面还有两个其他函数,它们返回一个 Promise (其中一个正在进行异步调用)和 BehaviorSubject >,可观察对象(通过数组中的 forEach 创建)与 forkJoin

我正在尝试模拟嵌套的 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 :(得分:0)

您只需要更改一些代码:

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

使用rejectsresolves要求您从Promise返回expect或使用async测试函数和await Promise