Javascript 开玩笑地模仿Lambda回调?(无法读取未定义的属性体)
我试图对lambda函数进行单元测试,但不知道如何模拟lambdaJavascript 开玩笑地模仿Lambda回调?(无法读取未定义的属性体),javascript,node.js,unit-testing,aws-lambda,jestjs,Javascript,Node.js,Unit Testing,Aws Lambda,Jestjs,我试图对lambda函数进行单元测试,但不知道如何模拟lambda回调,因此它会停止代码执行。正在调用回调I模型,对于lambda,它将立即返回响应。但在我的单元测试中,它继续执行代码,我得到错误: TypeError: Cannot read property 'body' of undefined 我对开玩笑还比较陌生,所以不知道如何继续 example.js(lambda代码) example.test.js(单元测试代码) 1-在根项目中添加文件夹 2-将文件got.js添加到\uuu
回调
,因此它会停止代码执行。正在调用回调
I模型,对于lambda,它将立即返回响应。但在我的单元测试中,它继续执行代码,我得到错误:
TypeError: Cannot read property 'body' of undefined
我对开玩笑还比较陌生,所以不知道如何继续
example.js
(lambda代码)
example.test.js
(单元测试代码)
1-在根项目中添加文件夹 2-将文件
got.js
添加到\uuuuu mocks\uuu
文件夹中
3-将代码添加到got.js
:
module.exports = {
post: (url, options) => {
return new Promise((res, rej) => {
res({ body: { active: 'test' } })
})
}
}
4-在测试文件中:
let example = require('./example');
let callback_arg1 = ''
let callback_arg2 = ''
let event = {
process: 1
};
let context = {};
let callback = (arg1, arg2) => {
callback_arg1 = arg1
callback_arg2 = arg2
};
describe('example', () => {
test('error calling process api', async () => {
await example.example(event, context, callback);
expect(callback_arg1).toBe(null)
expect(callback_arg2).toBe('Process 1 is: test')
});
});
您需要模拟
回调函数的实现。为了在错误处理后停止执行代码,您需要抛出新错误()
,并使用等待期望(例如.example(事件、上下文、回调)).rejects.toThrow(错误)
捕获错误以避免测试失败。通过这种方式,我们可以模拟aws lambda的行为
例如
example.js
:
module.exports = {
post: (url, options) => {
return new Promise((res, rej) => {
res({ body: { active: 'test' } })
})
}
}
const-got=require('got');
常量示例=异步(事件、上下文、回调)=>{
让信息;
常量选项={
json:{
过程:event.process,
},
responseType:'json',
};
const res=等待获得的邮政信息('https://some.url/api/process“,gotOptions)。捕获((错误)=>{
回调(错误);
});
console.log('process');
message=`Process${event.Process}是:${res.body.active};
回调(空,消息);
};
exports.example=示例;
example.test.js
:
module.exports = {
post: (url, options) => {
return new Promise((res, rej) => {
res({ body: { active: 'test' } })
})
}
}
const-example=require('./example');
const-got=require('got');
开玩笑。嘲弄(‘得到’);
const callback=jest.fn().mockImplementation((errorMsg)=>{
如果(errorMsg)抛出新错误(errorMsg);
});
const event={process:1};
const context={};
描述(“[示例]”,()=>{
测试('调用流程api时出错',异步()=>{
let error='error calling process';
got.post.mockRejectedValueOnce(错误);
wait expect(example.example(事件、上下文、回调)).rejects.toThrow(错误);
expect(callback).toHaveBeenCalledWith(error);
});
测试('should success',async()=>{
有一次({
正文:{active:true},
});
等待示例。示例(事件、上下文、回调);
expect(callback).toHaveBeenCalledWith(null,“进程1为:true”);
});
});
测试结果:
PASS examples/66567679/example.test.js
[example]
✓ error calling process api (5 ms)
✓ should success (10 ms)
console.log
process
at examples/66567679/example.js:17:11
------------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
------------|---------|----------|---------|---------|-------------------
All files | 100 | 100 | 100 | 100 |
example.js | 100 | 100 | 100 | 100 |
------------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests: 2 passed, 2 total
Snapshots: 0 total
Time: 3.966 s, estimated 4 s
看起来这里有两个问题
问题1
混合异步
和非异步
lambda函数可以是异步
或非异步
async
处理程序使用可以返回或抛出的async
函数。如果返回承诺
,lambda函数将等待承诺
解决或拒绝并返回结果
非异步
函数使用回调作为第三个参数,并返回传递给回调的结果
在这种情况下,函数是异步的,但也使用回调。它应该使用异步
函数或回调函数,但不能同时使用这两种函数
问题2
正在调用回调
I模型,对于lambda,它将立即返回响应
默认情况下,lambda函数不会在调用回调时立即返回响应
如果使用,请注意“执行将继续,直到事件循环为空或函数超时。在所有事件循环任务完成之前,响应不会发送到调用程序。”
(请注意,您可以设置为false
以使lambda函数立即返回,但这不是一个真正的解决方案,因为进程的状态将被冻结,并且在下次调用时将在该状态下重新启动,因此错误只会在下次调用时发生。)
因此,最佳实践是确保非异步
lambda函数始终能够运行到完成,因为传递到回调的值在事件循环为空之前不会实际传回
在上面的示例中,调用回调
后,执行可能会停止,但这只是因为AWS不会报告在调用带有错误的回调
后引发的异常信息
下面是一个简单的非异步处理程序来演示:
exports.handler = (event, context, callback) => {
console.log('starting'); // logged
callback('this error gets reported'); // callback called with an error
console.log('still running'); // logged
throw new Error('this error is not reported'); // not reported
console.log('ending'); // not logged
};
解决方案
在本例中,我只需删除回调
参数,然后使用纯异步
函数
大概是这样的:
const got = require('./got');
const example = async (event, context) => {
const gotOptions = {
json: {
process: event.process
},
responseType: 'json'
};
return got.post('https://some.url/api/process', gotOptions)
.then(res => `Process ${event.process} is: ${res.body.active}`)
.catch((error) => {
// log, format the returned error, etc.
// (or just remove the catch to return the error as-is)
console.log(error);
throw new Error(error);
});
};
exports.example = example;
const example = require('./example');
jest.mock('./got');
const got = require('./got');
// set default event
let event = {
process: 1
};
// set default context
const context = {};
// run before each test
beforeEach(() => {
// set default got.post response
got.post.mockReturnValue(Promise.resolve({
body: {
active: true
}
}));
});
// test artifact api
describe('[example]', () => {
test('error calling process api', async () => {
let error = 'error calling process';
// set got mock response for this test to error
got.post.mockReturnValue(Promise.reject(error));
// function we want to test w/ mock data
await expect(example.example(event, context)).rejects.toThrow(error); // SUCCESS
});
});
然后您可以像下面这样直接测试返回的Promise
:
const got = require('./got');
const example = async (event, context) => {
const gotOptions = {
json: {
process: event.process
},
responseType: 'json'
};
return got.post('https://some.url/api/process', gotOptions)
.then(res => `Process ${event.process} is: ${res.body.active}`)
.catch((error) => {
// log, format the returned error, etc.
// (or just remove the catch to return the error as-is)
console.log(error);
throw new Error(error);
});
};
exports.example = example;
const example = require('./example');
jest.mock('./got');
const got = require('./got');
// set default event
let event = {
process: 1
};
// set default context
const context = {};
// run before each test
beforeEach(() => {
// set default got.post response
got.post.mockReturnValue(Promise.resolve({
body: {
active: true
}
}));
});
// test artifact api
describe('[example]', () => {
test('error calling process api', async () => {
let error = 'error calling process';
// set got mock response for this test to error
got.post.mockReturnValue(Promise.reject(error));
// function we want to test w/ mock data
await expect(example.example(event, context)).rejects.toThrow(error); // SUCCESS
});
});
Jest支持测试使用回调的代码。您的测试可以接受done
参数
请参阅jest文档
将该模式应用到您的测试中,它可能如下所示:
describe('[example]', () => {
test('error calling process api', done => {
const error = 'error calling process';
got.post.mockReturnValue(Promise.reject(error));
await example.example(event, context, callbackError => {
// used try...catch pattern from jest docs
try {
expect(callbackError).toEqual(error);
} catch (e) {
done(e);
}
});
});
});
注释
测试不再是async
,并接受done
参数
期望值需要移到回调中
如果未调用done()
,测试将失败并超时
期望值需要一次尝试/抓住它。这是我从你那里拿的。这是回调中的expect().toEqual…
的结果。如果expect
失败,将不会调用done
,测试将超时,然后您将从expect
中获得超时错误,而不是更有用的错误
这将使你不必为了使用承诺而将其全部转换
一旦您对该测试和代码进行了一些操作,您可能会在主处理程序代码中遇到控制流错误
在catch中调用callback(error)
后,在