tutorial - Prueba de código asíncrono en elixir
elixir sinonimo (2)
Quiero probar una función que está utilizando Task.async
Para hacer que mi prueba pase, necesito hacerla dormir durante 100 ms antes de las afirmaciones, de lo contrario, el proceso de prueba se detiene antes de que se ejecute la tarea asíncrona.
¿Hay alguna manera mejor?
EDITADO, añadiendo ejemplos de código:
El código que me gustaría probar (aproximadamente):
def search(params) do
RateLimiter.rate_limit(fn ->
parsed_params = ExTwitter.Parser.parse_request_params(params)
json = ExTwitter.API.Base.request(:get, "1.1/search/tweets.json", parsed_params)
Task.async(fn -> process_search_output(json) end)
new_max_id(json)
end)
end
Y la prueba que ya escribí (trabajando solo con la llamada a dormir)
test "processes and store tweets" do
with_mock ExTwitter.API.Base, [request: fn(_,_,_) -> json_fixture end] do
with_mock TwitterRateLimiter, [rate_limit: fn(fun) -> fun.() end] do
TSearch.search([q: "my query"])
:timer.sleep(100)
# assertions
assert called TStore.store("some tweet from my fixtures")
assert called TStore.store("another one")
end
end
end
Como la pregunta es un poco vaga, daré la respuesta general aquí. La técnica habitual es monitorear el proceso y esperar el mensaje inactivo. Algo como esto:
task = Task.async(fn -> "foo" end)
ref = Process.monitor(task.pid)
assert_receive {:DOWN, ^ref, :process, _, :normal}, 500
Algunas cosas importantes:
El quinto elemento de la tupla es el motivo de salida. Estoy afirmando que la salida de la tarea es
:normal
. Cambie eso en consecuencia si está esperando otra salida.El segundo valor en
assert_receive
es el tiempo de espera. 500 milisegundos suena como una cantidad razonable dado que actualmente tiene un sueño de 100 ms.
Cuando no puedo usar el enfoque de José que involucra assert_receive
, uso un pequeño ayudante para hacer afirmaciones / suspensiones repetidamente, hasta que la afirmación pase o finalmente se agote.
Aquí está el módulo de ayuda
defmodule TimeHelper do
def wait_until(fun), do: wait_until(500, fun)
def wait_until(0, fun), do: fun.()
def wait_until(timeout, fun) defo
try do
fun.()
rescue
ExUnit.AssertionError ->
:timer.sleep(10)
wait_until(max(0, timeout - 10), fun)
end
end
end
Se puede usar así en el ejemplo anterior:
TSearch.search([q: "my query"])
wait_until fn ->
assert called TStore.store("some tweet from my fixtures")
assert called TStore.store("another one")
end