测试 pydantic 模型的约束

问题描述 投票:0回答:1

说我有一个模型

from pydantic import BaseModel

class Book(BaseModel):
  name: str
  description: str = Field(min_length=1, max_length=64, pattern="^[a-z]+$")

我想编写单元测试以确保模型已正确定义。 需要明确的是,我相信 pydantic 能够正确验证 is 的定义,我不相信我自己或我的同事能够始终正确定义模型约束,这是我们的业务需求。

为了测试这些约束/要求,我很想用错误的值实例化模型,然后捕获

ValidationError
,然后检查错误的内容以确保它是预期的错误。 比如:

def test_book__error_invalid_description():
  with pytest.raise(ValidationError) as err:
    Book(name="toto", description="123")
  assert "pattern_error" in err.value.errors # not working, that's just the idea

但是我发现这非常丑陋且难以维护。另外,我相信这主要是测试 pydantic 本身以及我的一点要求(甚至没有在这里测试模式要求的所有情况)。

我宁愿测试约束本身是否存在,但我找不到一种干净简洁的方法。 这是我尝试过的:

from annotated_types import MinLen, MaxLen

def test_book__validation_constraints():
  assert Book.model_fields["name"].is_required()
  assert MinLen(1) in Book.model_fields["description"].metadata
  assert MaxLen(64) in Book.model_fields["description"].metadata
  assert # no idea how to assert pattern

有更好的方法吗? 您认为测试此类需求是否太过分了?

python pydantic pydantic-v2
1个回答
0
投票

就我个人而言,我只是通过测试执行该模型的代码来隐式测试这一点。对于我来说,测试这样的序列化器/模型有点过于“单元化”。我在这个方向上的思考很大程度上是由这条思路决定的:https://kentcdodds.com/blog/write-tests

如果是我,假设这是验证 api 请求的正文,我只会编写使用有效和无效值来执行 api 的测试。对我来说,就这些测试真正为您的应用程序添加的内容而言,这是“最物有所值”。

如果您确实想要对模型进行单元测试,那么原始测试(即使用

pytest.raise
)在我看来是正确的方向。第二种方法是测试 pydantic 内部结构。如果 pydantic 改变了他们内部存储这些信息的方式,但模型仍然有效怎么办?您的测试将会中断,但您的代码仍然可以工作。我认为这是一条通往悲伤的黑暗之路,你应该尽量避免。

通常我们希望编写测试来增加我们对应用程序的信心。信心的反面是恐惧。就您而言,我们试图减轻的担心是,您的代码的任何部分依赖于此模型进行验证都将不起作用。假设这是验证 api 请求的正文,“可能发生的坏事”是有人发送请求,并且验证会错误地成功或失败。如果 pydantic 改变了它们的实现,这并不是“坏”。如果验证停止工作那就糟糕了。你的第一种方法在这里成功了,你的第二种方法却没有成功。

对于你的问题“如何确保字符串的最大长度定义为 12,而不创建字符串长度超过 12 的测试用例”:在我看来,你不应该这样做。这里的增值是什么?用长绳子进行测试有什么不好?为了避免这种情况,您要在代码库中添加什么样的复杂性?事实上,增加的复杂性可能会降低我们对应用程序的信心!善待 grug,因为 grug 就是你 https://grugbrain.dev/#grug-on-complexity

测试应该简单,而不是聪明。想想你怕坏什么,直接测试一下。

© www.soinside.com 2019 - 2024. All rights reserved.