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

Elixir测试中如何让结构体断言在缺失键时触发失败?

问题背景
  • 现有可正常通过的Elixir测试断言代码如下:
assert Contract.fetch(contract: valid_address) == %Contract{
         contract: "0x3D29Aa78fB558F84112bbC48a84F371147A920C9",
         name: "bla",
         price: nil,
         price_bnb: nil,
         call_id: 7,
         symbol: nil,
         transactions: []
       }
  • 现存的问题:
    • 断言中书写的%Contract{}结构体字面量,没有列出Contract结构体定义的全部键
    • 受Elixir结构体语法特性影响,未显式声明的字段会自动填充结构体定义的默认值,只要实际返回值的对应字段和默认值匹配,哪怕漏写字段,测试也能通过
    • 实际需求:只要断言没有覆盖Contract结构体的全部键,测试就主动触发失败,避免漏校验字段的问题
调整实现方案

核心校验逻辑

要实现目标,需要在原有等值比对之前,增加一层「字段覆盖完整性校验」,确保测试中手写的期望值结构体,没有靠默认值凑数、遗漏任何结构体定义的字段。
具体逻辑拆分为三步:

  1. 获取Contract结构体定义的全量键列表
    • 通过Contract.__struct__()可以拿到结构体定义的默认全量字段集合,排除内置的:__struct__元字段,就是需要全部覆盖的目标键集合
  2. 校验手写期望值的键集合和全量键集合完全一致
    • 如果存在差值(即部分键没有被显式写在期望值结构体里),直接触发断言失败,提示具体遗漏的字段
  3. 完成覆盖校验后,再执行原有的结构体等值比对

可直接落地的测试代码示例

# 1. 取Contract结构体定义的所有业务字段(排除内置元字段)
all_contract_fields =
  Contract.__struct__()
  |> Map.keys()
  |> List.delete(:__struct__)

# 2. 编写测试用的期望值(即原断言中的结构体内容)
expected = %Contract{
  contract: "0x3D29Aa78fB558F84112bbC48a84F371147A920C9",
  name: "bla",
  price: nil,
  price_bnb: nil,
  call_id: 7,
  symbol: nil,
  transactions: []
}

# 3. 字段覆盖校验:检查是否漏写字段
written_fields =
  expected
  |> Map.keys()
  |> List.delete(:__struct__)

assert written_fields == all_contract_fields,
       "断言未覆盖Contract的全部字段,缺失字段:#{inspect(all_contract_fields -- written_fields)}"

# 4. 执行原有的等值比对
assert Contract.fetch(contract: valid_address) == expected

复用性优化

如果多个测试用例都需要做全结构体覆盖校验,可以把上述逻辑抽成通用的测试助手函数,放到测试辅助模块里复用:

defmodule ContractCase do
  use ExUnit.CaseTemplate

  def assert_full_struct_equal(actual, %struct_module{} = expected) do
    all_fields =
      struct_module.__struct__()
      |> Map.keys()
      |> List.delete(:__struct__)

    written_fields =
      expected
      |> Map.keys()
      |> List.delete(:__struct__)

    missing = all_fields -- written_fields

    if missing != [] do
      ExUnit.Assertions.flunk("断言未覆盖#{struct_module}的全部字段,缺失字段:#{inspect(missing)}")
    end

    ExUnit.Assertions.assert actual == expected
  end
end

测试用例里可以直接调用这个助手函数,不需要重复写校验逻辑:

expected = %Contract{
  contract: "0x3D29Aa78fB558F84112bbC48a84F371147A920C9",
  name: "bla",
  price: nil,
  price_bnb: nil,
  call_id: 7,
  symbol: nil,
  transactions: []
}

assert_full_struct_equal(Contract.fetch(contract: valid_address), expected)

实现效果说明:这种校验逻辑会强制要求在断言里显式写出所有结构体字段,哪怕某字段的实际值和结构体默认值完全一致,只要没在断言中声明对应字段,就会触发测试失败。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.22 11:36:04