Reputation: 41
I have a Product
schema which has a UPI(unique product identifier)
eg. A985748BNG6784C
. This is an autogenerated unique product identifier.
I have a function upi_generate()
which calls another external function gen_nano_id()
to generate this random unique id.
If by chance, the id generated by gen_nano_id()
has already been generated, the function upi_generate()
calls itself recursively till the time gen_nano_id()
generates a unique id. Thus generating a unique UPI
.
gen_nano_id()
can sometimes return duplicate ids and for that purpose I have written the below code with recursive call.
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
Now, I have to test the id regeneration logic for duplicate ids.
My testing approach involves following logic.
Create a two products with duplicate UPI
and try to reach the _
part of the case comparison.
For this purpose, I have mocked(I don't control the behaviour of this function) the gen_nano_id()
.
Now, the problem that I am facing is mocking results in creation of always the same ids no matter what and I go in an infinite loop.
I am not able to figure out a way to reach the exit condition(nil)
part of case comparison with this mocking approach of gen_nano_id
.
Upvotes: 1
Views: 109
Reputation: 121010
There is great writing by José Valim Mocks and explicit contracts. It says
always consider “mock” to be a noun, never a verb
That means you should not mock the generation function. You’d better create a generator and mock it.
Somewhat like that:
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
Now you are all set to return whatever you want from the generator.
Upvotes: 3