Javascript 开玩笑地模仿Lambda回调?(无法读取未定义的属性体)

Javascript 开玩笑地模仿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

我试图对lambda函数进行单元测试,但不知道如何模拟lambda
回调
,因此它会停止代码执行。正在调用
回调
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)
    后,在