开始之前 这个问题看起来比较大,所以你可以浏览一下当前的方法,然后直接进入问题,在问题末尾用粗体标记。
我正在为项目中的一些处理程序编写测试。主要功能之一是在数据库中创建/更改/删除对象。
由于某些原因,我们不想编写代码来用某些逻辑来断言数据库中的突变(例如,在函数完成后,应该创建一个对象,该对象应该包含这样的字段值,让我们获取其创建的 id,然后看看,它的还创建了依赖项。另外,让我们检查是否没有删除其他对象,等等)
为了现在断言正确的数据库突变,我们使用以下方法:
我们有加载所有当前数据库内容的功能
def get_db_content():
result = []
for table in TABLE_NAME_LIST:
rows = db.fetch(f"SELECT * FROM {table}")
for row in rows:
result.append({"tablename": table, "fields": row})
return result
创建列表,其中每个条目都包含表名,每个字段都具有特定对象的值。
此外,我们还有包含预期状态的 json 编码数据的夹具数据文件:
[
{
"tablename": "users",
"fields": {
"id": 1,
"name": "Valt25"
}
}
]
在编写测试时,我们需要准备这样的夹具,并带有预期的数据,并进行基本比较:
assert get_db_content() == load_fixture("expected_db_state.json")
这很好用,如果我们在排序方面遇到问题,我们可以在 sql 中添加显式排序。
正如您在预期的数据库状态中看到的,用户 id 是一个整数值,很可能是串行的,我们无法事先知道这个值,幸运的是我们使用 uuid,在处理程序运行时生成。我们可以模拟 uuid 生成器函数,以期望数据库中具有特定 id 的新对象。
但是,我想不再在运行时生成东西,这些东西可能是在数据库级别生成的(例如uuid、创建/更新的日期戳),因此在比较过程中无法获得期望的某些数据。
为了解决这个问题,我们有下一个方法(实际上是修改第一个方法):
我们有部分比较内容的辅助函数。因此,如果某些字段出现在数据库中,但未出现在预期数据中,那么就可以了。
所以现在,
get_db_content()
调用将返回下一个结果:
[
{
"tablename": "positions",
"fields": {
"id": 127,
"name": "position name",
"date_created": "2025-01-01T17:52:21"
"date_updated": "2025-01-01T17:52:21"
}
},
{
"tablename": "employees",
"fields": {
"id": 256,
"position_id": 127,
"name": "user name",
"date_created": "2024-12-31T10:45:21"
"date_updated": "2025-01-01T17:52:21"
}
}
]
如您所见,此处预设了 id 和图章。但在赛程数据中我们有这样的东西:
[
{
"tablename": "positions",
"fields": {
"name": "position name"
}
},
{
"tablename": "employees",
"fields": {
"name": "user name"
}
}
]
正如您所看到的,没有任何自动生成的字段。下一行将成功断言:
assert partial_compare(get_db_content(), load_fixture("expected_db_state.json"))
问题从这里开始
正如您所看到的,以前的方法甚至可以在没有自动生成字段的情况下断言数据,但它不能断言数据库对象关系,但这是存储在任何地方的对象的相当重要的特征。为了克服这个问题,我们编写了相当复杂的代码,每个任务都是唯一的。
但是,我想以某种方式关联夹具文件中的对象。像这样的东西:
[
{
"tablename": "positions",
"$tag": "created_position",
"fields": {
"name": "position name"
}
},
{
"tablename": "employees",
"ref_fields": ["position_id"]
"fields": {
"position_id": "$created_position.id"
"name": "user name"
}
}
]
如您所见,首先我用一些字符串标记位置。然后在员工对象中,我标记字段“position_id”不应通过值断言,但其值应从与指定标签关联的真实数据库对象中获取。并且还表明,“position_id”字段应与标有“created_position”标签的对象中的“id”字段相同。
问题本身
我确信我自己可以实现这样的事情,但是我预计这里会遇到很多陷阱。这就是为什么我写这个问题,也许有类似的方法,解决同样的问题,如果你不知道,也许你可以在这里看到陷阱?
PS。代码是用 python 编写的,但也可以是任何编程语言。
PS2。这里编写的任何代码都没有经过测试,因为它是为了展示方法而编写的,而不是为了实现特定功能。
不太确定实现这种数据库状态验证的最佳方法是什么。
首先想到的一件事是将从数据库接收到的数据加入某种聚合中:
SELECT employees.name AS name, positions.name AS position
FROM employees
JOIN positions ON employees.position_id = positions.id;
然后将该数据与简单的预期值数组进行比较。例如
expected_employees.json
:
[
{
"name": "Michael Scott",
"position": "Regional Manager"
},
{
"name": "Dwight Schrute",
"position": "Salesman"
},
...
]
要验证数据库的完整状态,您可能需要多个不同的查询和相应的固定文件。
这种方法的一个优点是您可以保持实际的比较逻辑非常简单。如果排除自动生成的值,可能就像调用 Python 的
assertEqual
一样简单。