首页 > 解决方案 > 测试一个长生不老药功能

问题描述

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

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

如果偶然生成的 idgen_nano_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 无论如何,我进入无限循环。

我无法找到一种方法来exit condition(nil)使用gen_nano_id.

标签: elixirphoenix-framework

解决方案


José Valim Mocks和明确的合同有很好的写作。它说

始终将“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

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


推荐阅读