【问题标题】:How to mock dynamodb promise support using jest?如何使用 jest 模拟 dynamodb 承诺支持?
【发布时间】:2021-11-04 06:43:55
【问题描述】:

similar questions but all of them uses some kind of library(在我的情况下不起作用)。我如何让我为通过 dynamodb 查询返回的承诺工作

这是我要测试的代码

  const isUrl = require('is-url');
const AWS = require('aws-sdk');
const { nanoid } = require('nanoid/async');
const express = require('express');

const router = express.Router();
const dynamoDb = new AWS.DynamoDB.DocumentClient();

// URL from users

router.post('/', async (req, res) => {
  // urlId contains converted short url characters generated by nanoid

  const urlId = await nanoid(8);
  const { longUrl } = req.body;

  // Veryfying url Format using isUrl, this return a boolean
  const checkUrl = isUrl(longUrl);
  if (checkUrl === false) {
    return res.status(400).json({ error: 'Invalid URL, please try again!!!' });
  }

  const originalUrl = longUrl;
  const userType = 'anonymous'; // user type for anonymous users
  const tableName = 'table-name'; // table name for storing url's

  const anonymousUrlCheckParams = {
    TableName: tableName,
    Key: {
      userId: userType,
      originalUrl,
    },
  };

  const paramsForTransaction = {
    TransactItems: [
      {
        Put: {
          TableName: tableName,
          Item: {
            userId: userType,
            originalUrl,
            convertedUrl: `https://url/${urlId}`,
          },
        },
      },

      {
        Put: {
          TableName: tableName,
          Item: {
            userId: urlId,
            originalUrl,
          },
          ConditionExpression: 'attribute_not_exists(userId)',
        },
      },
    ],
  };

  try {
    const dynamoDbGetResults = await dynamoDb
      .get(anonymousUrlCheckParams)
      .promise();
    if (
      Object.keys(dynamoDbGetResults).length === 0 &&
      dynamoDbGetResults.constructor === Object
    ) {
      await dynamoDb.transactWrite(paramsForTransaction).promise();
      return res.status(201).json({
        convertedUrl: `https://trimify.awssensei.tk/${urlId}`,
      });
    }
    return res.status(201).json({
      convertedUrl: dynamoDbGetResults.Item.convertedUrl,
    });
  } catch (err) {
    return res
      .status(500)
      .json({ error: 'Unknown Server Error, Please Trimify Again!' });
  }
});

module.exports = 路由器;

我的测试代码

describe('Test for Authenticated Url', () => {
  beforeEach(() => {
    jest.resetModules();
  });
  test('should return converted url for authenticated user', async () => {
    const mockedDocumentClient = {
      get: jest.fn(),
      transactWrite: jest.fn(),
    };

    const mockedDynamoDB = {
      DocumentClient: jest.fn(() => mockedDocumentClient),
    };
    jest.doMock('aws-sdk', () => ({ DynamoDB: mockedDynamoDB }));

    mockedDocumentClient.get.mockImplementation((params, callback) => {
      const data = {
        Item: {
          convertedUrl: 'xyz',
        },
      };
      callback(null, data);
    });
    mockedDocumentClient.transactWrite.mockImplementation(
      (params, callback) => {
        callback(null);
      }
    );

    const app = require('../app');
    const response = await request(app).post('/auth-ops/convert').send({
      longUrl: 'https://google.com',
      userId: 'google-oauth2|110198332436292901252',
    });
    expect(response.body).toMatchObject({ convertedUrl: 'xyz' });
    expect(response.statusCode).toBe(201);
  });
test('should send 201 when it is new Url', async () => {
    const mockedDocumentClient = {
      get: jest.fn(),
      transactWrite: jest.fn(),
    };

    const mockedDynamoDB = {
      DocumentClient: jest.fn(() => mockedDocumentClient),
    };
    jest.doMock('aws-sdk', () => ({ DynamoDB: mockedDynamoDB }));

    mockedDocumentClient.get.mockImplementation((params, callback) => {
      const data = {};
      callback(null, data);
    });
    mockedDocumentClient.transactWrite.mockImplementation(
      (params, callback) => {
        callback(null);
      }
    );

    const app = require('../app');
    const response = await request(app).post('/auth-ops/convert').send({
      longUrl: 'https://google.com',
      userId: 'google-oauth2|110198332436292901252',
    });
    expect(response.body).not.toBe(undefined);
    expect(response.statusCode).toBe(201);
  });
});

【问题讨论】:

    标签: javascript express jestjs amazon-dynamodb


    【解决方案1】:

    你应该用 mock 重现 dynamodb 签名(我的例子只是一般性的,idk 你 dynamo 导入的所有细节)

    jest.mock('dynamodb', () => {
      get: async (data) => jest.fn()
    })
    

    【讨论】:

    • 你能告诉我使用 aws-sdlk-mock 库还是我试图模拟的方式
    • @JatinMehrotra 你可以试试固本,请看这篇文章amitgupta-gwl.medium.com/mock-dynamodb-services-5192cdcc450f
    • 或者如你所说的 aws-sdk-mock ` var AWS = require('aws-sdk-mock'); AWS.mock('DynamoDB', 'putItem', function (params, callback){ callback(null, "成功将项目放入数据库"); }); /** 测试 **/ AWS.restore('DynamoDB'); `
    【解决方案2】:

    我删除了明确的特定代码并使用您的逻辑创建了一个示例,我添加了 3 个 cmets,“CASE A”、“CASE B”和“CASE C”,它们与您代码中的所有分支(例如 ifs或返回),我们将在我们的测试用例中使用它

    let AWS = require("aws-sdk");
    
    let dynamoDb = new AWS.DynamoDB.DocumentClient({ apiVersion: "2012-08-10" });
    
    async function main() {
      try {
        const dynamoDbGetResults = await dynamoDb.get("some-loginUrlCheckParams").promise();
        // CASE A
        if (Object.keys(dynamoDbGetResults).length === 0 && dynamoDbGetResults.constructor === Object) {
          let urlId = await dynamoDb.transactWrite("some-paramsForTransaction").promise();
          return {
            convertedUrl: `https://my-url/${urlId}`,
          };
        }
        // CASE B
        return {
          convertedUrl: dynamoDbGetResults.Item.convertedUrl,
        };
      } catch (err) {
        // CASE C
        return { error: "Unknown Server Error, Please Trimify Again!", message: err.message };
      }
    }
    
    module.exports = { main };
    

    从这里开始,我们需要在每个测试中访问 .get.transactWrite,这样我们就可以模拟在特定情况下返回的值

    要实现这一点,您可以利用以 mock* 开头的 jest mock 和提升变量的工作原理

    创建一个 api 来操作每个测试用例的返回值:

    let AWS = require("aws-sdk");
    
    let { main } = require("./main");
    
    let mockDocumentClient = {
      get: {
        promise: jest.fn()
      },
      transactWrite: {
        promise: jest.fn()
      },
    };
    
    jest.mock("aws-sdk", () => {
      return {
        DynamoDB: {
          DocumentClient: jest.fn().mockImplementation(() => {
            return {
              get: () => mockDocumentClient.get,
              transactWrite: () => mockDocumentClient.transactWrite,
            };
          }),
        },
      };
    });
    
    describe("Test for Authenticated Url", () => {
      it("returns CASE A", async () => {
        mockDocumentClient.get.promise.mockReturnValueOnce({})
        mockDocumentClient.transactWrite.promise.mockReturnValueOnce("test-urlId")
        let test = await main();
        expect(test).toEqual({ convertedUrl: 'https://my-url/test-urlId' })
      });
    
      it("returns CASE B", async () => {
        mockDocumentClient.get.promise.mockReturnValueOnce({
          Item: {
            convertedUrl: "test-converted-url"
          }
        })
        let test = await main();
        expect(test).toEqual({ convertedUrl: 'test-converted-url' })
      });
    
      it("returns CASE C", async () => {
        mockDocumentClient.get.promise.mockImplementationOnce(() => {
          throw new Error("test-error")
        })
        let test = await main();
        expect(test).toEqual({ error: 'Unknown Server Error, Please Trimify Again!', message: "test-error" })
      });
    });
    

    如您所见,我们将mockDocumentClient 延迟加载到jest.mock 中(Jest 只会在get: () => 的函数执行时调用mockDocumentClient),使我们能够将相同的模拟变量传递给我们的测试用例并操作返回值

    在该 sn-p 中实现 100% 的测试覆盖率:

    我强烈建议您阅读这两篇文章,他们将从 Jest 的角度进行更详细的解释:

    https://jestjs.io/docs/es6-class-mocks#calling-jestmock-with-the-module-factory-parameter

    https://blog.bam.tech/developer-news/fix-jest-mock-cannot-access-before-initialization-error

    【讨论】:

    • 很抱歉我编辑了我的问题,但对 jest 及其强大的嘲讽功能仍然很陌生,您再解释一下或检查我的编辑
    • 从测试的角度来看没有任何变化@JatinMehrotra,因为 aws-sdk 库仍然相同,您可以按照我在回答中提供的模拟结构来实现您的要求
    • 我会试试你的代码。你能花点时间解释一下吗let mockDocumentClient = { get: { promise: jest.fn() }, transactWrite: { promise: jest.fn() }, }; jest.mock("aws-sdk", () => { return { DynamoDB: { DocumentClient: jest.fn().mockImplementation(() => { return { get: () => mockDocumentClient.get, transactWrite: () => mockDocumentClient.transactWrite, }; }), }, }; });
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-10-07
    • 1970-01-01
    • 2017-02-26
    • 2019-06-19
    • 2019-09-18
    • 1970-01-01
    • 2017-09-04
    相关资源
    最近更新 更多