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

Elixir WithClauseError问题:单元测试匹配{:ok, value}失败

问题分析与解决方案

你遇到的核心问题是**with语句的模式匹配错误**,同时函数的错误返回逻辑也不符合你的预期。让我一步步拆解并解决:

1. 根本错误:with子句的模式不匹配

你的验证函数(比如validate_times)返回的是原子:ok,但你在with里用了元组{:ok}去匹配——这两个是完全不同的类型!

  • :ok 是一个单独的原子
  • {:ok} 是包含一个原子的元组

当validate_times返回:ok时,with的第一个匹配子句直接失败,进入else块。但你的else只处理{:error, error}的情况,无法匹配传入的:ok,所以抛出WithClauseError。

2. 错误返回逻辑不符合需求

原来的else块用IO.puts error,这个函数的返回值是:ok,导致参数错误时函数返回:ok而非你想要的{:error, reason}。


修正后的完整代码

主函数修正

def calculate_price(start_time, end_time, zone, payment_type) do
  with(
    :ok <- validate_times(start_time, end_time),
    :ok <- validate_zone(zone),
    :ok <- validate_payment_type(payment_type)
  ) do
    elapsed_minutes = div(Time.diff(end_time, start_time), 60)
    cond do
      zone == "A" && elapsed_minutes <= 15 -> {:ok, 0}
      zone == "B" && elapsed_minutes <= 90 -> {:ok, 0}
      zone == "A" && elapsed_minutes > 15 && payment_type == "hourly" -> {:ok, calc(elapsed_minutes - 15, 2, 60)}
      zone == "B" && elapsed_minutes > 90 && payment_type == "hourly" -> {:ok, calc(elapsed_minutes - 90, 1, 60)}
      zone == "A" && elapsed_minutes > 15 && payment_type == "real" -> {:ok, calc(elapsed_minutes - 15, 0.16, 5)}
      zone == "B" && elapsed_minutes > 90 && payment_type == "real" -> {:ok, calc(elapsed_minutes - 90, 0.08, 5)}
    end
  else
    {:error, error} -> {:error, error}
    # 兜底处理意外情况,避免再次抛出WithClauseError
    unexpected -> {:error, "Unexpected error: #{inspect(unexpected)}"}
  end
end

# 以下验证函数保持不变,它们的返回值是正确的
defp validate_times(start_time, end_time) when end_time > start_time, do: :ok
defp validate_times(_start_time, _end_time), do: {:error, "The start/end time is wrong"}
defp validate_zone(zone) when zone == "A" or zone == "B", do: :ok
defp validate_zone(_zone), do: {:error, "The zone is wrong"}
defp validate_payment_type(payment_type) when payment_type == "hourly" or payment_type == "real", do: :ok
defp validate_payment_type(_payment_type), do: {:error, "The payment type is wrong"}
defp calc(minutes_to_pay, price_per_minutes, minutes_per_price_increment) do
  cond do
    rem(minutes_to_pay, minutes_per_price_increment) > 0 -> (div(minutes_to_pay, minutes_per_price_increment) + 1) * price_per_minutes
    true -> div(minutes_to_pay, minutes_per_price_increment) * price_per_minutes
  end
end

测试用例验证

你的原有测试用例现在可以正常通过:

test "calculate price; zone: B, paymentType: real" do
  # 4小时30分钟的时间差
  startTime = ~T[12:00:00.000]
  endTime = ~T[16:30:00.000]
  zone = "B"
  paymentType = "real"
  assert {:ok, 2.88} == calculate_price(startTime, endTime, zone, paymentType)
end

你还可以新增错误场景的测试,比如:

test "calculate price with invalid zone" do
  start_time = ~T[12:00:00.000]
  end_time = ~T[13:00:00.000]
  assert {:error, "The zone is wrong"} == calculate_price(start_time, end_time, "C", "real")
end

关键修正点总结

  1. 将with中的{:ok} <-改为:ok <-,匹配验证函数返回的原子类型
  2. 修改else块,直接返回{:error, error}元组,符合你的需求
  3. 添加兜底的else子句,避免因意外值再次抛出WithClauseError

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 08:50:32