WithClauseError,e剂测试与(:ok,value}

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

我正在尝试在Elixir中编写安全的功能代码,并使用单元测试来确认我的代码可以正常工作。这是控制器代码:

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) no with clause matching: :ok

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

我的长生不老药版本是1.9.1。

testing functional-programming elixir tdd with-statement
2个回答
1
投票

问题在这里:

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

[该方法返回裸露的:ok原子,当您尝试将其与{:ok}中的单个元素元组with进行模式匹配时,显然会失败。

那将起作用:

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

此外,请使用mix format任务根据准则设置代码格式,否则很难阅读。这也适用于with中的后续子句。


Sidenote:这就是我们可能会使用模式匹配和防护措施以更精妙的方式重写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

1
投票

首先检查您的代码是否可以手动工作。从错误消息中,我相信您的代码在逻辑中的某个地方有错误,并且永远不会返回{:ok, ...}。可能是Datetime。通常使用DateTime.compare()进行比较。

最快的检查方法是将结果与变量进行模式匹配并声明该变量。 Mix将向您显示返回的结果以及您的预期结果。

result = calculate_price(startTime, endTime, zone, paymentType)
assert result == {:ok, 2.88}
© www.soinside.com 2019 - 2024. All rights reserved.