【问题标题】:Testing an elixir function测试一个长生不老药功能
【发布时间】:2019-02-03 08:48:09
【问题描述】:

我有一个 Product 架构,它有一个 UPI(unique product identifier) 例如。 A985748BNG6784C。这是一个自动生成的唯一产品标识符。

我有一个函数upi_generate(),它调用另一个外部函数gen_nano_id() 来生成这个随机的唯一ID。

如果偶然gen_nano_id()生成的id已经生成,函数upi_generate()会递归调用自己,直到gen_nano_id()生成一个唯一的id。从而生成一个唯一的UPI

gen_nano_id() 有时会返回重复的 id,为此我使用递归调用编写了以下代码。

  def gen_nano_id() do  //external function
      Nanoid.generate(10, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ")
  end

  # TODO: write test case for this
  def upi_generate() do // required function
      upi = "A" <> gen_nano_id() <> "C"

      case get_product_with_upi(upi) do
         nil ->
            upi 
         _ ->
            upi_generate()
      end
  end

  // Check if product with UPI already exists
  defp get_product_with_upi(upi) do
      from(p in "snitch_products", select: p.upi, where: p.upi == ^upi) 
      |> Repo.one()
  end

现在,我必须测试重复 ID 的 ID 重新生成逻辑。

我的测试方法涉及以下逻辑。 创建具有重复UPI 的两个产品并尝试达到案例比较的_ 部分。

为此,我模拟了(我不控制此函数的行为)gen_nano_id()

现在,我面临的问题是模拟导致创建始终相同的 id 无论如何,我进入无限循环。

gen_nano_id 的这种模拟方法相比,我无法找到一种方法来达到exit condition(nil) 部分案例比较。

【问题讨论】:

  • 如果你需要唯一键,为什么不使用 UUID 模块,它没有这样的问题,你总是得到唯一键hex.pm/packages/uuid
  • @fad3d 因为我想自定义生成的 uuid,使其仅由数字和大写字母组成。

标签: elixir phoenix-framework


【解决方案1】:

José Valim Mocks and explicit contracts 写的很棒。它说

始终将“mock”视为名词,而不是动词


这意味着你不应该模拟生成函数。你最好创建一个生成器并模拟它。

有点像:

defmodule Generator do
  @callback gen_id :: integer()
end

defmodule NanoGenerator do
  @moduledoc "Used in dev/prod"
  @behaviour Generator

  @impl true
  def gen_id() do
    get_nano_id() # external function or whatever
  end
end

defmodule TestGenerator do
  @moduledoc "Used in test"
  @behaviour Generator

  use Agent # to store state
  @ids ~w|foo foo bar|

  @impl true
  def gen_id() do
    id = # get the counter from Agent, and increase it
    @ids[id]
  end 
end

现在你已经准备好从生成器返回任何你想要的了。

【讨论】:

  • 您建议的方法是重新定义测试的 gen_id() 函数并控制其对 TestGenerator 模块的行为。但我想要的是在不改变函数行为的情况下测试 upi_generated 重复的条件。
  • 我从不建议重新定义任何东西(例如,因为您的代码根本没有gen_id 函数。)我建议的是隔离与外部资源的互操作(NanoWhatever ,) 将get_nano_id 包装成您自己的Generator 并模拟它。这是在 Elixir 中执行此操作的首选方式。
猜你喜欢
  • 1970-01-01
  • 2017-10-21
  • 1970-01-01
  • 1970-01-01
  • 2016-02-01
  • 2010-09-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多