为什么在 运行 读取模拟 AWS 文件的测试时 jest 超时?

Why does jest time out when running a test on mock AWS file read?

我正在尝试编写单元测试(开玩笑)来测试服务的执行情况。目前,测试超时,我肯定它与我创建读取流的要点有关。我嘲笑 s3.getObject.createReadStream() 正确吗?

handler.ts

const XLSX = require("xlsx");
const AWS = require("aws-sdk");
import { Pool } from "pg";

class Service {
    client: any;
    workbook: any = "";

    constructor(client) {
        this.workbook = "";
        this.client = client;
    }

    async createWorkbookFromS3(filePath: string, id: string) {
    
        const fileLocation = id + filePath;
    
        const params = {
          Bucket: "bucket",
          Key: fileLocation,
        };
    
        function getBufferFromS3(callback) {
          const buffers = [];
          const s3 = new AWS.S3();
          const stream = s3.getObject(params).createReadStream();
          stream.on("data", (data) => buffers.push(data));
          stream.on("end", () => callback(null, Buffer.concat(buffers)));
          stream.on("error", (error) => callback(error));
        }
    
        function getBufferFromS3Promise() {
          return new Promise((resolve, reject) => {
            getBufferFromS3((error, s3buffer) => {
              if (error) {
                return reject(error)
              };
              return resolve(s3buffer);
            });
          });
        }
        try {
          const buffer = await getBufferFromS3Promise();
          this.workbook = XLSX.read(buffer);
        } catch (e) {
          if (e) {
            this.client.release()
            console.error(e);
            throw new Error("Unable to read the xlsx from S3");
          }
        }
      }
}


const decoder = async (event: any) => {
    const id: string = event.body["id"];
    const filePath = `${id}.xlsx`;
  
    try {
      switch (event.path) {
        case "/vsiDecoder/draft":
          try {
            const filePath: string = event.body["filePath"];
  
            const dbCredentials = {"user":"postgres","password":"password","host":"awsconnectihoststring","port": "0000" ,"database":"db"}
            const pool = new Pool(JSON.parse(dbCredentials['SecretString']));
            const client = await pool.connect();
            const vsiObj = new Service(client);
  

            await vsiObj.createWorkbookFromS3(filePath, id);
  
            vsiObj.client.release()
            pool.end();
  
          } catch (e) {
            throw new Error(e)
          }
  
          return {
            statusCode: 200,
            message: "The document has been successfully drafted.",
          };

        default:
          return {
            message: "Bad request",
            statusCode: 400,
          };
      }
    } catch (e) {
      console.log(e)
      return{
        statusCode: 400,
        message: e.message,
      }
    }
  };

  module.exports = {Service, decoder }

handler.test.ts

const decoder = require('./handler.ts')
import { Pool } from 'pg';

const mockgetObject = {
    createReadStream: jest.fn(() => {
        return {
            on: jest.fn()
        }
    })
}
const mockS3Instance = {
    putObject: jest.fn().mockReturnThis(),
    promise: jest.fn().mockReturnThis(),
    catch: jest.fn(),
    getObject: jest.fn(() => mockgetObject)
}
jest.mock('aws-sdk', () => {
    return {
        S3: jest.fn(() => mockS3Instance),
    }
});

jest.mock('pg', () => {
    const mClient = {
        connect: jest.fn().mockReturnThis(),
        query: jest.fn().mockReturnThis(),
        end: jest.fn().mockReturnThis(),
        release: jest.fn().mockReturnThis(),
    };
    return { Pool: jest.fn(() => mClient) };
});

describe('Service Test', () => {
    let client;
    beforeEach(() => {
        client = new Pool();
        jest.setTimeout(30000);
    });
    afterEach(() => {
        jest.clearAllMocks();
    });

    it('should executre decoder', async () => {
        const id = 'id';
        const filePath = 'filePath'
        const service = new decoder.Service(client);
        await service.createWorkbookFromS3(id, filePath)
        const events = { 'body': { 'id': '1234', "path": "/decoder/draft", "vsiFilePath": "path" } };
        const response = '{"statusCode":200,"message":"The document has been succesfully drafted."}';
        expect((await decoder.decoder(events)).body).toEqual(response);
    })
})

错误:

 Service Test › should executre decoder

    : Timeout - Async callback was not invoked within the 30000 ms timeout specified by jest.setTimeout.Timeout - Async callback was not invoked within the 30000 ms timeout specified by jest.setTimeout.Error:

感谢任何帮助!我确实相信我成功地模拟了 createReadStream 但如果有一种方法可以模拟 getBufferFromS3Promise 那将是可以接受的。

在您的 createReadStream mock 中,on 函数没有实现。但是,在 getBufferFromS3(callback) 的代码中,on 函数负责设置执行回调函数的事件处理程序,这是来自 getBufferFromS3Promise 的 Promise 将被解决或拒绝的地方。因为该承诺永远不会被解决或拒绝,所以您的异步测试代码永远不会完成。

您需要实现 on 函数的模拟,它将以某种方式执行事件处理程序回调。如果您只是测试成功案例,您可以在 on 内同步执行处理程序,因为 'end' 事件恰好在 'error' 事件之前连接。更好的方法可能是通过 on 保存处理程序来模拟事件发射器,并向流模拟添加 triggerEvent 函数,允许您在测试中强制触发不同的流事件。