Burlando llamadas consecutivas de funciones en Elixir con Mock o Mox
Estoy intentando simular múltiples llamadas a una función para que devuelva un valor específico diferente cada vez. No estoy tan familiarizado/a con Elixir ni con los conceptos funcionales.
defmodule Roller do
def roll do
1..10
|> Enum.random()
end
end
Roller devuelve un número aleatorio en cada llamada.
defmodule VolunteerFinder do
import Roller
def find(list) do
find(list, [])
end
defp find([], result) do
result
end
defp find([head | tail], result) do
result = [%{name: head, score: Roller.roll()} | result]
find(tail, result)
end
end
Así que, suponiendo que la lista contenga más de un elemento, el roller se llama 2 veces. En mi prueba, necesito controlarlo de alguna manera.
He intentado hacerlo con Mock. Me gustaría hacer algo como esto de la manera más simple posible. Sería genial no tener que guardar ningún estado en ningún lugar ni ejecutar procesos separados para cada llamada. Sé que el pensamiento de Elixir puede ser un poco diferente al paradigma objetivo que tengo. ¿Cuál es la forma más correcta de Elixir de probar el módulo VolunteerFinder
?
defmodule VolunteerFinderTest do
use ExUnit.Case
import Mock
import Roller
test "Encontrar voluntario para una lista con un elemento" do
with_mock("Roller",
Roller,
[roll: fn() -> 5 end]
) do
assert VolunteerFinder.find(["John"]) == [%{name: "John", score: 5}]
end
end
test "Encontrar voluntario para una lista con dos elementos" do
with_mock("Roller",
Roller,
[roll: fn
() -> 2
() -> 5
end]
) do
assert VolunteerFinder.find(["John", "Andrew"])
== [%{name: "Andrew", score: 5}, %{name: "John", score: 2}]
end
end
end
davy.ai
Para simular funciones en Elixir, puedes usar el módulo
Mock
que es proporcionado por la biblioteca estándar. El móduloMock
te permite crear una implementación falsa de una función que se puede utilizar en tus pruebas en lugar de la implementación real.Para simular una función que devuelve un valor diferente en cada llamada, puedes usar la función
calls()
en la definición de la simulación. La funcióncalls()
toma una lista de tuplas donde cada tupla contiene los argumentos que se pasaron a la función y el valor que debe devolverse para esos argumentos.Aquí hay una versión actualizada del módulo
VolunteerFinderTest
que utiliza una implementación simulada de la funciónRoller.roll()
:En este ejemplo, creamos una simulación del módulo
Roller
y especificamos que queremos simular la funciónroll()
pasando[:roll]
como segundo argumento a la funciónmock()
. Luego usamos la funciónexpect()
para definir el comportamiento de la función simulada.En la primera prueba, usamos
fn() -> 5 end
para especificar que la función simuladaroll()
siempre debe devolver 5. En la segunda prueba, usamosfn() -> Enum.take([2, 5, 9], 2) end
para especificar que la función simuladaroll()
debe devolver 2 en la primera llamada y 5 en la segunda llamada.Después de definir la implementación simulada, podemos llamar a la función
find()
enVolunteerFinder
y verificar que devuelva lo que esperamos. También usamos la funciónassert_received()
para asegurarnos de que la función simuladaroll()
haya sido llamada con los argumentos correctos. Pasamostimes: 2
como opción aassert_received()
en la segunda prueba porque la funciónfind()
llama aroll()
dos veces.