尝试用 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 函数?
答案 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
。
使用rejects
和resolves
要求您从Promise
返回expect
或使用async
测试函数和await
Promise
。