typescript - 如何使用 TypeScript Partials 测试 AWS Lambda?
问题描述
非常类似于Using partial shape for unit testing with typescript但我不明白为什么 Partial 类型被视为与完整版本不兼容。
我有一个单元测试,如果body
AWS lambda 事件无效,则检查 lambda 是否返回 400。为了避免给我的同事制造噪音,我不想invalidEvent
使用 full 的所有属性进行创建APIGatewayProxyEvent
。因此使用Partial<APIGatewayProxyEvent>
.
it("should return 400 when request event is invalid", async () => {
const invalidEvent: Partial<APIGatewayProxyEvent> = {
body: JSON.stringify({ foo: "bar" }),
};
const { statusCode } = await handler(invalidEvent);
expect(statusCode).toBe(400);
});
该const { statusCode } = await handler(invalidEvent);
行编译失败:
Argument of type 'Partial<APIGatewayProxyEvent>' is not assignable to parameter of type 'APIGatewayProxyEvent'.
Types of property 'body' are incompatible.
Type 'string | null | undefined' is not assignable to type 'string | null'.
Type 'undefined' is not assignable to type 'string | null'.ts(2345)
我知道APIGatewayProxyEvent
身体可以string | null
(从查看类型)但是string | null | undefined
从哪里来?为什么我的body
- 这是一个字符串 - 不是一个有效的身体APIGatewayProxyEvent
如何使用 TypeScript Partials 测试 AWS Lambda?
我可以as
用来做类型断言,但我发现 Partials 更明确。以下代码虽然有效:
const invalidEvent = { body: JSON.stringify({ foo: "bar" }) } as APIGatewayProxyEvent;
更新:使用 Omit 和 Pick 创建一个新类型
type TestingEventWithBody = Omit<Partial<APIGatewayProxyEvent>, "body"> & Pick<APIGatewayProxyEvent, "body">;
it("should return 400 when request event is invalid", async () => {
const invalidEvent: TestingEventWithBody = { body: JSON.stringify({ foo: "bar" }) };
const { statusCode } = await handler(invalidEvent);
expect(statusCode).toBe(400);
});
失败:
Argument of type 'TestingEventWithBody' is not assignable to parameter of type 'APIGatewayProxyEvent'.
Types of property 'headers' are incompatible.
Type 'APIGatewayProxyEventHeaders | undefined' is not assignable to type 'APIGatewayProxyEventHeaders'.
Type 'undefined' is not assignable to type 'APIGatewayProxyEventHeaders'.ts(2345)
解决方案
我不明白为什么 Partial 类型被视为与完整版本不兼容
从根本上说,这是不可避免的——你从需要body
属性的东西开始string | null
,然后创建了要求更弱的东西string | null | undefined
。在这种情况下,您确实提供了body
,但这并不重要,因为handler
仅invalidEvent
通过Partial<APIGatewayProxyEvent>
接口查看,编译器知道该属性可能丢失。如您所见,如果您修补该属性以再次需要它,它只会抱怨下一个属性。
在您不拥有handler
API 的情况下,您实际上只有三个选择,其中没有一个是理想的:
- 实际上提供了一个完整的
APIGatewayProxyEvent
(见最后的快捷方式); - 向编译器声明您的测试对象是
APIGatewayProxyEvent
带有类型断言的完整对象;或者 // @ts-ignore
用注释告诉编译器根本不要检查它。
使用Partial
通常只是选项 2 中的一个步骤,使用:
const thing: Partial<Thing> = { ... };
whatever(thing as Thing);
代替:
const thing = { ... } as Thing;
whatever(thing);
如果您拥有 handler
的 API,那么最好的方法是应用接口隔离原则,并具体说明它实际需要什么来完成其工作。如果只是body
,例如:
type HandlerEvent = Pick<APIGatewayProxyEvent, "body">;
function handler(event: HandlerEvent) { ... }
fullAPIGatewayProxyEvent
仍然是 的有效参数handler
,因为它确实具有 a body
(并且它也具有其他属性的事实是无关紧要的,它们无法通过 访问HandlerEvent
)。这也可以作为关于您从完整对象中实际使用的内容的内置文档。
在您的测试中,您现在可以创建较小的对象:
it("should return 400 when request event is invalid", async () => {
const invalidEvent: HandlerEvent = { body: JSON.stringify({ foo: "bar" }) };
const { statusCode } = await handler(invalidEvent);
expect(statusCode).toBe(400);
});
作为奖励,如果后来发现您需要访问更多事件的内部属性handler
,您可以更新类型:
type HandlerEvent = Pick<APIGatewayProxyEvent, "body" | "headers">;
并且您会在需要更新测试数据以考虑到这一点的任何地方遇到错误。不会发生这种情况const invalidEvent = { ... } as APIGatewayProxyEvent;
,您必须通过查看哪些测试在运行时失败来跟踪更改。
我见过的与选项 1 一起使用的另一个快捷方式是在部分周围包装一个函数,提供合理的默认值:
function createTestData(overrides: Partial<APIGatewayProxyEvent>): APIGatewayProxyEvent {
return {
body: null,
headers: {},
// etc.
...overrides,
};
}
it("should return 400 when request event is invalid", async () => {
const invalidEvent = createTestData({ body: JSON.stringify({ foo: "bar" }) });
const { statusCode } = await handler(invalidEvent);
expect(statusCode).toBe(400);
});
在这种情况下,您应该使默认值尽可能小(null
, 0
, ""
, 空对象和数组,...),以避免依赖于它们的任何特定行为。
推荐阅读
- java - 为什么 addNode() 方法返回编译错误“方法...类型...不适用于参数...”,我该如何解决?
- biztalk - 为什么 BizTalk Scope 没有捕获此 MissingPropertyException
- python - 如何在python中搜索和访问子元素和结构
- excel - 运行时错误 91 与 Excel VBA 中的 HTML 文档
- apache-calcite - Calcite:我们可以重写优化的 RelNode 吗?
- c# - 如何拆分字符串并删除每个第 n 个元素?
- transactions - 向客户端公开 DynamoDb 乐观锁定版本以防止更高级别的竞争条件是否可能并且是一种好习惯?
- paypal - 在处理 3 中使用 Paypal Here 信用卡扫描仪播放声音
- ecmascript-6 - 我可以将纯 javascript es6 与 ASP、NET CORE MVC 多页应用程序一起使用吗?
- excel - 编译错误:过程太大需要帮助