testing - WithClauseError, Elixir 单元测试匹配(:ok,值}

标签 testing functional-programming elixir tdd with-statement

我正在尝试在 elixir 中编写安全的功能代码,并使用单元测试来确认我的代码是否正常工作。这是 Controller 代码:

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} -> IO.puts error
    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

controller_test 代码:

test "calculate price; zone: B, paymentType: real" do
    # 4 hours and 30 minute difference
    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

对于这段代码,我试图验证传入的参数是否正确,以便在我的代码的正确路径上返回 {:ok, value} 的结果。如果参数错误我想知道为什么会发生错误。目前我只是打印到命令行,但最终我想返回 {:error, reason}。只是将 {:error, error} 放在 else 子句中会导致不同的错误。

测试用例的结果是: ** (WithClauseError) 没有 with 子句匹配::ok

我认为这意味着我的 calculate_price 函数正在返回 {:ok}。我不明白为什么要返回 with 子句中的值,而不是 doelse 子句中的值!

我的 elixir 版本是 1.9.1。

最佳答案

问题在这里:

{:ok} <- validate_times(start_time, end_time)

该方法返回裸 :ok 原子,而您尝试将其模式匹配到 with 中的单个元素元组 {:ok} ,显然失败了。

那行得通:

with :ok <- validate_times(start_time, end_time),
     :ok <- validate_zone(zone),
     :ok <- validate_payment_type(payment_type) do
  ...
end

此外,使用mix format 任务根据指南格式化您的代码,否则将非常难以阅读。 这也适用于 with 中的后续子句。


旁注:这就是我们如何使用模式匹配和守卫以更精巧的方式重写您的 cond 子句:

case {zone, elapsed_minutes, payment_type} do
  {"A", elapsed_minutes, _} when elapsed_minutes <= 15 -> {:ok, 0}
  {"B", elapsed_minutes, _} when elapsed_minutes <= 90 -> {:ok, 0}
  {"A", _, "hourly"} -> {:ok, calc(elapsed_minutes - 15, 2, 60)}
  {"B", _, "hourly"} -> {:ok, calc(elapsed_minutes - 90, 1, 60)}
  {"A", _, "real"} -> {:ok, calc(elapsed_minutes - 15, 0.16, 5)}
  {"B", _, "real"} -> {:ok, calc(elapsed_minutes - 90, 0.08, 5)}
end

关于testing - WithClauseError, Elixir 单元测试匹配(:ok,值},我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59107196/

相关文章:

elixir - 用于测试的 Phoenix 端点服务器无法启动

javascript - 使用 Jasmine Specs 比较对象

android - 如何为 androidTest.apk 重命名 outputFileName?

mongodb - Clojure - 如何在单元测试中正确挂载状态

java - 了解 Java 8/9 函数式编程(lambda 表达式)中的 Map 和 Reduce。 map() 和 reduce() 如何提高性能?

elixir - :ok init argument in Elixir's GenServer example?有什么意义

elixir - 如何设置 Ecto 变更集以防止保存缺少必填字段的模型?

javascript - 与独立的 Mocha 相比,使用 Karma 运行测试有什么优势

javascript - 如何使用 lo-dash 以函数式编程方式获得最小的数字(不在数组中)?

javascript - 在 JavaScript 中处理可选返回值的最佳方法是什么?