You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

带有数据库依赖的TypeScript Lambda Handler测试方案咨询

解决TypeScript Lambda Handler测试时无法Mock数据库的方案

你的核心问题在于Lambda Handler内部硬编码了myService和myRepository的实例化逻辑,导致测试时无法替换为Mock对象。以下两种成熟方案可以解决这个问题,无需依赖本地Docker或内存数据库:


方案一:重构Handler支持依赖注入(推荐)

通过打破Handler内部的硬编码依赖,让外部可以传入Mock实例,同时保持生产环境的正常逻辑。

方式1:给Handler添加可选依赖参数

修改Handler代码,将依赖作为可选参数传入,默认使用真实实现:

import { APIGatewayProxyCallback } from "aws-lambda";
import { myService } from "./service/myService";
import { myRepository } from "./repository/myRepository";

// 定义依赖类型,确保类型安全
type HandlerDependencies = {
  service?: myService;
};

export const lambdaHandler = async (
    event: any,
    context: any,
    callback: APIGatewayProxyCallback,
    deps: HandlerDependencies = {},
): Promise<void> => {
    // 优先使用传入的依赖,否则创建真实实例
    const service = deps.service || new myService(new myRepository());
    // 注意:原代码漏了await,异步方法需等待执行结果
    const res = await service.execute(event);

    callback(null, {
        statusCode: 200,
        body: JSON.stringify(res),
    });
};

测试时直接传入Mock的Service:

import { lambdaHandler } from "./handler";
import { myService } from "./service/myService";

// 创建Mock Service实例
const mockService = {
    execute: jest.fn().mockResolvedValue({ data: "模拟返回数据" })
} as unknown as myService;

test("Lambda Handler返回模拟数据", async () => {
    const mockCallback = jest.fn();
    await lambdaHandler({}, {}, mockCallback, { service: mockService });

    // 验证回调被正确调用
    expect(mockCallback).toHaveBeenCalledWith(null, {
        statusCode: 200,
        body: JSON.stringify({ data: "模拟返回数据" })
    });
    // 验证Service的execute方法被调用
    expect(mockService.execute).toHaveBeenCalledWith({});
});

方式2:用依赖工厂函数解耦

创建单独的依赖工厂模块,Handler通过工厂获取依赖,测试时Mock工厂函数:

  1. 创建deps.ts:
import { myService } from "./service/myService";
import { myRepository } from "./repository/myRepository";

export const getService = () => new myService(new myRepository());
  1. 修改Handler:
import { APIGatewayProxyCallback } from "aws-lambda";
import { getService } from "./deps";

export const lambdaHandler = async (
    event: any,
    context: any,
    callback: APIGatewayProxyCallback,
): Promise<void> => {
    const service = getService();
    const res = await service.execute(event);

    callback(null, {
        statusCode: 200,
        body: JSON.stringify(res),
    });
};
  1. 测试代码:
import { lambdaHandler } from "./handler";
import * as deps from "./deps";
import { myService } from "./service/myService";

test("Lambda Handler使用模拟Service", async () => {
    const mockService = {
        execute: jest.fn().mockResolvedValue({ result: "测试数据" })
    } as unknown as myService;

    // Mock工厂函数,返回模拟Service
    jest.spyOn(deps, "getService").mockReturnValue(mockService);

    const mockCallback = jest.fn();
    await lambdaHandler({}, {}, mockCallback);

    expect(deps.getService).toHaveBeenCalled();
    expect(mockService.execute).toHaveBeenCalled();
    expect(mockCallback).toHaveBeenCalledWith(null, {
        statusCode: 200,
        body: JSON.stringify({ result: "测试数据" })
    });
});

方案二:直接Mock依赖模块(无需大改业务代码)

如果不想重构Handler,可使用测试框架的模块Mock功能(比如Jest的jest.mock),直接替换myRepository或myService模块的实现,让Handler内部实例化时得到Mock对象。

Mock myService模块

import { lambdaHandler } from "./handler";
import { myService } from "./service/myService";

// Mock整个myService模块
jest.mock("./service/myService", () => {
    return {
        myService: jest.fn().mockImplementation(() => {
            return {
                execute: jest.fn().mockResolvedValue({ data: "模拟结果" })
            };
        })
    };
});

test("Lambda Handler调用模拟Service", async () => {
    const mockCallback = jest.fn();
    await lambdaHandler({}, {}, mockCallback);

    // 验证myService被实例化
    expect(myService).toHaveBeenCalled();
    // 获取模拟实例,验证方法调用
    const mockServiceInstance = myService.mock.instances[0];
    expect(mockServiceInstance.execute).toHaveBeenCalledWith({});
    // 验证返回结果
    expect(mockCallback).toHaveBeenCalledWith(null, {
        statusCode: 200,
        body: JSON.stringify({ data: "模拟结果" })
    });
});

Mock myRepository模块

import { lambdaHandler } from "./handler";
import { myRepository } from "./repository/myRepository";

// Mock整个myRepository模块
jest.mock("./repository/myRepository", () => {
    return {
        myRepository: jest.fn().mockImplementation(() => {
            // 模拟Repository的核心方法
            return {
                query: jest.fn().mockResolvedValue([{ id: 1, name: "模拟数据" }])
            };
        })
    };
});

test("Lambda Handler调用模拟Repository", async () => {
    const mockCallback = jest.fn();
    await lambdaHandler({}, {}, mockCallback);

    // 验证Repository被实例化
    expect(myRepository).toHaveBeenCalled();
    // 获取模拟实例,验证方法调用
    const mockRepoInstance = myRepository.mock.instances[0];
    expect(mockRepoInstance.query).toHaveBeenCalled();
});

方案对比

  • 方案一:符合依赖注入设计原则,代码可测试性、扩展性更强,是长期维护的最佳选择。
  • 方案二:无需修改业务代码,适合快速编写测试,但模块Mock可能会导致测试与模块耦合度较高。

内容的提问来源于stack exchange,提问作者Sheen

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.15 05:20:28