2015-02-26 9 views
15

Chcę przetestować funkcję, która korzysta Task.asyncTestowanie kodu asynchronicznego w Elixir

Aby uczynić mój test zdać, muszę zrobić to spać przez 100ms przed twierdzeniami, w przeciwnym razie proces testowy zostanie zabity przed async zadanie jest wykonywane.

Czy istnieje lepszy sposób?

edytować, dodając próbki kodu:

Kod Chciałbym przetestować (w przybliżeniu):

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 

a test już pisałem (działa tylko z wezwaniem do spania)

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 
+1

Może pokażesz nam minimalną uszkodzoną przykład, który ilustruje szczególnego rodzaju twierdzenia, które chcesz robić? –

+0

Przykładowy kod bardzo pomógłby w uzyskaniu dobrej odpowiedzi. –

+0

OK właśnie dodane próbki kodu – Chris

Odpowiedz

30

Ponieważ pytanie jest nieco niejasne, udzielę ogólnej odpowiedzi tutaj. Zwykłą techniką jest monitorowanie procesu i oczekiwanie na wiadomość w dół. Coś takiego:

task = Task.async(fn -> "foo" end) 
ref = Process.monitor(task.pid) 
assert_receive {:DOWN, ^ref, :process, _, :normal}, 500 

Kilka ważnych rzeczy:

  • Piąty element krotki jest powód wyjście. Zapewniam, że wyjście z zadania to :normal. Zmień to odpowiednio, jeśli spodziewasz się kolejnego wyjścia.

  • Druga wartość w assert_receive to limit czasu. 500 milisekund wyda się rozsądną kwotą, biorąc pod uwagę, że obecnie masz sen 100 ms.

+0

Dziękuję Ci José! Właśnie dodałem kilka próbek kodu do mojego pytania. Chyba muszę sprawić, by funkcja wyszukiwania zwróciła krotkę zawierającą pid zadania? (Nie lubię zmieniać mojego kodu tylko w celach testowych: /) – Chris

+9

Pozwól mi argumentować na korzyść tego. Twój test jest konsumentem twojego kodu, tak jak każdej innej części twojej aplikacji. Zapewnienie prawidłowego wyniku testów jest ogólnie dobrym wskaźnikiem, inne części aplikacji będą również poprawnie używać tego kodu. Na przykład, patrząc na testy, nie mam pojęcia, jakie są wyniki powrotu. Co się stanie, jeśli osiągnę limit API? Co się stanie, jeśli nie będę? Wydaje się, że funkcja polega na efektach ubocznych, a powrót zadania wskazywałoby: hej, skończę to później, obejrzyj to zadanie, jeśli się o to zatroszczysz. –

+0

Rozumiem, dziękuję! – Chris

7

Kiedy nie można używać podejścia Jose udziałem assert_receive, używam małego pomocnika wielokrotnie zrobić twierdzenie/uśpienia, aż do przełęczy twierdzenie czy wreszcie czas na zewnątrz.

Oto moduł pomocnik

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 

Może być stosowany tak w dawnej przykład:

TSearch.search([q: "my query"]) 
wait_until fn -> 
    assert called TStore.store("some tweet from my fixtures") 
    assert called TStore.store("another one") 
end