简体   繁体   中英

How to mock AWS TimestreamWrite by jest

This project is to record data by AWS Timestream, and it works well.

However, I'm failed to mock AWS TimestreamWrite by using jest. I tried some ways but not working. Can someone help me?

My files as below:

ledger-service.js

const AWS = require("aws-sdk");
const enums = require("./enums");

var https = require("https");
var agent = new https.Agent({
  maxSockets: 5000,
});
const tsClient = new AWS.TimestreamWrite({
  maxRetries: 10,
  httpOptions: {
    timeout: 20000,
    agent: agent,
  },
});

module.exports = {
  log: async function (audit) {
    try {
      if (Object.keys(audit).length !== 0) {
        if (!isPresent(audit, "name")) {
          throw new Error("Name shouldn't be empty");
        }

        if (!isPresent(audit, "value")) {
          throw new Error("Value shouldn't be empty");
        }

        return await writeRecords(recordParams(audit));
      } else {
        throw new Error("Audit object is empty");
      }
    } catch (e) {
      throw new Error(e);
    }
  },
};
function isPresent(obj, key) {
  return obj[key] != undefined && obj[key] != null && obj[key] != "";
}
function recordParams(audit) {
  const currentTime = Date.now().toString(); // Unix time in milliseconds
  const dimensions = [
    // { Name: "client", Value: audit["clientId"] },
    { Name: "user", Value: audit["userId"] },
    { Name: "entity", Value: audit["entity"] },
    { Name: "action", Value: audit["action"] },
    { Name: "info", Value: audit["info"] },
  ];

  return {
    Dimensions: dimensions,
    MeasureName: audit["name"],
    MeasureValue: audit["value"],
    MeasureValueType: "VARCHAR",
    Time: currentTime.toString(),
  };
}
function writeRecords(records) {
  try {
    const params = {
      DatabaseName: enums.AUDIT_DB,
      TableName: enums.AUDIT_TABLE,
      Records: [records],
    };

    return tsClient.writeRecords(params).promise();
  } catch (e) {
    throw new Error(e);
  }
}

ledger-service.spec.js

const AWS = require("aws-sdk");
const audit = require("./ledger-service");

describe("ledger-service", () => {

    beforeEach(async () => {
        jest.resetModules();
    });
  
    afterEach(async () => {
      jest.resetAllMocks();
    });

    it("It should write records when all success", async () => {
        const mockAudit={
            name: 'testName',
            value: 'testValue',
            userId: 'testUserId',
            entity: 'testEntity',
            action: 'testAction',
            info: 'testInfo',
        };

        const mockWriteRecords = jest.fn(() =>{
            console.log('mock success')
            return { promise: ()=> Promise.resolve()}
         });

        const mockTsClient={
            writeRecords: mockWriteRecords
        }

        jest.spyOn(AWS,'TimestreamWrite');
        AWS.TimestreamWrite.mockImplementation(()=>mockTsClient);

        //a=new AWS.TimestreamWrite();
        //a.writeRecords();   //these two lines will pass the test and print "mock success"

        await audit.log(mockAudit); //this line will show "ConfigError: Missing region in config"

        expect(mockWriteRecords).toHaveBeenCalled();
    });
});

I just think the the AWS I mocked doesn't pass into the ledger-service.js . Is there a way to fix that?

Thanks

updates: Taking hoangdv's suggestion

I am thinking jest.resetModules(); jest.resetAllMocks(); jest.resetModules(); jest.resetAllMocks(); don't work. If I put the "It should write records when all success" as the first test, it will pass the test. However, it will fail if there is one before it.

Pass

  it("It should write records when all success", async () => {
    const mockAudit = {
      name: 'testName',
      value: 'testValue',
      userId: 'testUserId',
      entity: 'testEntity',
      action: 'testAction',
      info: 'testInfo',
    };

    await audit.log(mockAudit);

    expect(AWS.TimestreamWrite).toHaveBeenCalledWith({
      maxRetries: 10,
      httpOptions: {
        timeout: 20000,
        agent: expect.any(Object),
      },
    });
    expect(mockWriteRecords).toHaveBeenCalled();
  });

  it("It should throw error when audit is empty", async () => {
    const mockAudit = {};

    await expect(audit.log(mockAudit)).rejects.toThrow(`Audit object is empty`);
  });

Failed

  it("It should throw error when audit is empty", async () => {
    const mockAudit = {};

    await expect(audit.log(mockAudit)).rejects.toThrow(`Audit object is empty`);
  });

  it("It should write records when all success", async () => {
    const mockAudit = {
      name: 'testName',
      value: 'testValue',
      userId: 'testUserId',
      entity: 'testEntity',
      action: 'testAction',
      info: 'testInfo',
    };

    await audit.log(mockAudit);

    expect(AWS.TimestreamWrite).toHaveBeenCalledWith({
      maxRetries: 10,
      httpOptions: {
        timeout: 20000,
        agent: expect.any(Object),
      },
    });
    expect(mockWriteRecords).toHaveBeenCalled();
  });

In ledger-service.js you call new AWS.TimestreamWrite "before" module.exports , this means it will be called with actual logic instead of mock.

The solution is just mock AWS before you call require("./ledger-service");

ledger-service.spec.js

const AWS = require("aws-sdk");

describe("ledger-service", () => {
  let audit;
  let mockWriteRecords;

  beforeEach(() => {
    mockWriteRecords = jest.fn(() => {
      return { promise: () => Promise.resolve() }
    });

    jest.spyOn(AWS, 'TimestreamWrite');
    AWS.TimestreamWrite.mockImplementation(() => ({
      writeRecords: mockWriteRecords
    }));

    audit = require("./ledger-service"); // this line
  });

  afterEach(() => {
    jest.resetModules(); // reset module to update change for each require call
    jest.resetAllMocks();
  });

  it("It should write records when all success", async () => {
    const mockAudit = {
      name: 'testName',
      value: 'testValue',
      userId: 'testUserId',
      entity: 'testEntity',
      action: 'testAction',
      info: 'testInfo',
    };

    await audit.log(mockAudit);

    expect(AWS.TimestreamWrite).toHaveBeenCalledWith({
      maxRetries: 10,
      httpOptions: {
        timeout: 20000,
        agent: expect.any(Object),
      },
    });
    expect(mockWriteRecords).toHaveBeenCalled();
  });
});

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM