首页  >  问答  >  正文

Sinon实现对AWS DynamoDB连接调用的模拟

我在 API Gateway 中有一个端点,它映射到 AWS 中的 Lambda 函数。在为端点的新处理程序函数编写测试用例时,我不希望 spec 文件调用实际 API 或连接到 DynamoDB。我尝试添加 sinon.stub,但它仍然调用连接到 DynamoDB 并且测试用例失败。我无法找到存根出错的地方。

处理程序.js:

saveUser(userName, logger) {
  const Item = {
    id: uuid.v4(),
    userName,
    ttl: parseInt(Date.now() / 1000) + 900 // expire the name after 15 minutes from now
  };
  const params = {
    TableName: "my-table-name",
    Item
  };
  logger.log(`Saving new user name to DynamoDB: ${JSON.stringify(params)}`);

  return new Promise(function(resolve, reject) {
    db.put(params, function(err, _) {
      if (err) {
        logger.exception(`Unable to connect to DynamoDB to create: ${err}`);
        reject({
          statusCode: 404,
          err
        });
      } else {
        logger.log(`Saved data to DynamoDB: ${JSON.stringify(Item)}`);
        resolve({
          statusCode: 201,
          body: Item
        });
      }
    });
  });
}

Handler.spec.js:

import AWS from "aws-sdk";
const db = new AWS.DynamoDB.DocumentClient({
  apiVersion: "2012-08-10"
});

describe("user-name-handler", function() {
  const sandbox = sinon.createSandbox();
  afterEach(() => sandbox.restore());

  it("Test saveUser() method", async function(done) {
    const {
      saveUser
    } = userHandler;

    sandbox.stub(db, "put")
      .returns(new Promise((resolve, _) => resolve({
        statusCode: 200
      })));

    try {
      const result = await saveUser("Sample User", {
        log: () => {},
        exception: () => {}
      });

      expect(result).to.be.equal({
        data: "some data"
      });
      done();
    } catch (err) {
      console.log(err);
      done();
    }
  });
});

错误:

Error: Resolution method is overspecified. Specify a callback *or* return a Promise; not both.

我通过控制台记录了 err 对象,它给了我这个错误,这让我认为它正在尝试连接到 DynamoDB。

Error: connect ENETUNREACH 127.0.0.1:80
  at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1144:16) {
message: 'Missing credentials in config, if using AWS_CONFIG_FILE, set AWS_SDK_LOAD_CONFIG=1',       
errno: 'ENETUNREACH',
code: 'CredentialsError',
syscall: 'connect',
address: '127.0.0.1',
port: 80,
time: 2023-05-07T10:45:25.835Z,
originalError: {
  message: 'Could not load credentials from any providers',
  errno: 'ENETUNREACH',
  code: 'CredentialsError',
  syscall: 'connect',
  address: '127.0.0.1',
  port: 80,
  time: 2023-05-07T10:45:25.835Z,
  originalError: [Object]
}

相关:如何测试从 AWS DynamoDB 返回数据的方法

P粉633733146P粉633733146211 天前319

全部回复(2)我来回复

  • P粉714780768

    P粉7147807682024-03-22 18:19:12

    您正在嘲笑测试文件中声明的 db - 而不是 saveUser 实际使用的 db

    解决方案是将 db 声明移至其自己的模块,例如:db.js

    const AWS = require("aws-sdk");
    
    const db = new AWS.DynamoDB.DocumentClient({
      apiVersion: "2012-08-10"
    });
    
    module.exports = db;

    然后从 saveUser 模块和测试中导入它 - 这样我们就可以模拟 saveUser 使用的同一个 db 实例。

    更新

    我能够使用以下代码成功运行测试:

    测试代码:

    const sinon = require('sinon');
    const { saveUser } = require('../userHandler');
    const { expect } = require('chai');
    
    const db = require('../db');
    
    describe('user-name-handler', function() {
      afterEach(() => sinon.restore());
    
      it('Test saveUser() method', async function() {
    
          sinon.stub(db, 'put')
            .returns(new Promise((resolve, _) => resolve({
                statusCode: 201,
                body: 'some data'
          })));
    
          try {
              const result = await saveUser(db, 'Sample User', {
                log: () => {},
                exception: () => {}
              });
    
              expect(result).to.deep.equal({
                statusCode: 201,
                body: 'some data'
              });
          } catch (err) {
            console.log('err', err);
          }
      });
    });

    用户处理程序文件:

    const db = require('./db');
    
    const saveUser = (db, userName, logger) => {
      const Item = {
        id: uuid.v4(),
        userName,
        ttl: parseInt(Date.now() / 1000) + 900 // expire the name after 15 minutes from now
      };
      const params = {
        TableName: "my-table-name"
      };
      logger.log(`Saving new user name to DynamoDB: ${JSON.stringify(params)}`);
    
        return db.put(params, function(err, _) {
          if (err) {
            logger.exception(`Unable to connect to DynamoDB to create: ${err}`);
            return reject({
              statusCode: 404,
              err
            });
          } else {
            logger.log(`Saved data to DynamoDB: ${JSON.stringify(Item)}`);
            return resolve({
              statusCode: 201,
              body: Item
            });
          }
        });
    }
    
    module.exports = { saveUser };

    package.json

    {
      "name": "play",
      "version": "1.0.0",
      "description": "",
      "main": "index.js",
      "scripts": {
        "test": "mocha --timeout 5000"
      },
      "author": "",
      "license": "ISC",
      "dependencies": {
        "aws-sdk": "^2.1373.0",
        "chai": "^4.3.7",
        "mocha": "^10.2.0",
        "sinon": "^15.0.4"
      }
    }

    输出

    回复
    0
  • P粉476046165

    P粉4760461652024-03-22 16:16:37

    在文件中分离数据库连接

    我们可以将数据库连接分离到不同的文件中,并将其导入到处理程序实现以及 spec 文件中。

    db.js

    import AWS from "aws-sdk";
    const db = new AWS.DynamoDB.DocumentClient({ apiVersion: "2012-08-10" });
    
    export default db;
    

    yields() 函数

    存根不应直接返回 Promise,而应与 .yields() 及其回调将接受的参数链接。我们可以更改参数以覆盖代码的各个分支。

    代码

    describe("user-handler connection success", function () {
        const sandbox = sinon.createSandbox();
        afterEach(() => sandbox.restore());
        before(() => {
            sinon.stub(db, "put")
                .yields(null, true);
            sinon.stub(db, "get")
                .yields(null, { sampleKey: "sample value" });
            sinon.stub(db, "delete")
                .yields(null, { sampleKey: "sample value" });
        });
        after(() => {
            db.put.restore();
            db.get.restore();
            db.delete.restore();
        });
    
        it("Test saveUser() method success", async function () {
            const result = await userHandler.saveToken("sample user", {
                log: () => {},
                exception: () => {}
            });
    
            expect(result.statusCode).to.be.equal(201);
        });
    });
    

    有用的链接

    https://www.youtube.com/watch?v=vXDbmrh0xDQ

    回复
    0
  • 取消回复