Testovanie
Testovanie je dôležitou súčasťou vývoja softvéru. V tejto lekcii sa pozrieme na to, ako testovať náš Elixir kód pomocou knižnice ExUnit a na testovacie best practices.
ExUnit
Elixir má zabudovaný testovací framework ExUnit, ktorý obsahuje všetko, čo potrebujeme na dôkladné otestovanie nášho kódu. Než s ním začneme, je dôležité spomenúť, že ExUnit testy sa implementujú ako Elixir skripty, takže pre súbory s testami musíme použiť príponu .exs. Pred spustením testov ešte musíme naštartovať samotný ExUnit s ExUnit.start(). Bežne sa to robí v súbore test/test_helper.exs.
Keď sme si v minulej lekcii vygenerovali nový projekt, mix nám vygeneroval rovno aj jednoduché základné testy v súbore test/example_test.exs.
defmodule ExampleTest do
use ExUnit.Case
doctest Example
test "greets the world" do
assert Example.hello() == :world
end
end
Kompletnú sadu testov nášho projektu môžeme spustiť pomocou príkazu mix test. Následne by sme mali dostať podobný výstup:
..
Finished in 0.03 seconds
2 tests, 0 failures
Prečo sú vo výstupe dva testy? Pozrime sa na lib/example.ex. Mix tam pre nás vytvoril ďalší test, a to doctest.
defmodule Example do
@moduledoc """
Documentation for Example.
"""
@doc """
Hello world.
## Examples
iex> Example.hello
:world
"""
def hello do
:world
end
end
assert
Každý kto sa už niekedy stretol s automatickými testami softvéru, určite pozná príkaz assert (v niektorých testovacích frameworkoch should, či expect).
Toto makro testuje, či sa daný výraz vyhodnotí ako true. Ak nie, vyhodí chybu a test zlyhá. Pozrime sa na príklad takéhoto zlyhania - upravme náš test a spustime ho príkazom mix test:
defmodule ExampleTest do
use ExUnit.Case
doctest Example
test "greets the world" do
assert Example.hello() == :word
end
end
Teraz by sme mali vidieť celkom iný výstup:
1) test greets the world (ExampleTest)
test/example_test.exs:5
Assertion with == failed
code: assert Example.hello() == :word
left: :world
right: :word
stacktrace:
test/example_test.exs:6 (test)
.
Finished in 0.03 seconds
2 tests, 1 failures
ExUnit nám povie úplne presne, kde (na ktorom asserte) testy zlyhali, aká bola očakávaná hodnota a aká bola skutočná hodnota.
refute
Opakom príkazu assert je príkaz refute. Použijeme ho v prípade, že chceme testovať, či sa daný výraz vyhodnotí ako false.
assert_raise
Niekedy potrebujeme testovať, že kód vyhodí chybu (prípadne konkrétny typ chyby) - na to sa nám hodí príkaz assert_raise. Príklad použitia assert_raise je v lekcii o knižnici Plug.
assert_receive
V Elixire, aplikácie pozostávajú z actorov/procesov, ktorí posielajú správy medzi sebou a často chceme otestovať odosielanú správu. Keďže ExUnit beží ako vlastný proces, môže prijímať správy tak ako každý iný proces a môžeme assertnúť pomocou makra assert_received:
defmodule SendingProcess do
def run(pid) do
send(pid, :ping)
end
end
defmodule TestReceive do
use ExUnit.Case
test "receives ping" do
SendingProcess.run(self())
assert_received :ping
end
end
assert_received nečaká na správy, s assert_receive však môžeme špecifikovať kedy čas na prijatie správy vyprší.
capture_io and capture_log
Zachytávanie výstup aplikácie je možné s pomocou ExUnit.CaptureIO bez zmien v pôvodnej aplikácii. Jednoducho vložíme funkciu, ktorá generuje výstup ako argument:
defmodule OutputTest do
use ExUnit.Case
import ExUnit.CaptureIO
test "outputs Hello World" do
assert capture_io(fn -> IO.puts "Hello World" end) == "Hello World\n"
end
end
ExUnit.CaptureLog je ekvivalent použitý, keď chceme zachytávať výstup do Logger.
Test Setup
Niekedy potrebujeme pred samotnými testami vykonať pomocné činnosti. S tým nám pomôžu makrá setup a setup_all. Blok kódu v makre setup sa vykoná pred každým jednotlivým testom, blok v makre setup_all sa vykoná len raz - na začiatku pred spustením celej sady testov. Oba bloky by mali vrátiť tuple v tvare {:ok, stav}, pričom stav bude dostupný v jednotlivých testoch.
Ako príklad si do nášho súboru s testami doplníme blok setup_all:
defmodule ExampleTest do
use ExUnit.Case
doctest Example
setup_all do
{:ok, recipient: :world}
end
test "greets", state do
assert Example.hello() == state[:recipient]
end
end
Mockovanie
Jednoduchá odpoveď na mockovanie v Elixire je: nerobte to. Inštinktívne možno siahnete na mocky, ale v komunita Elixiru dôrazne neodporúča z dobrého dôvodu.
Na dlhšiu diskusiu je tu výborný článok.
Ak vo svojom kóde dodrživate zásady dobrého funkcionálneho návrhu, mockovanie nikdy potrebovať nebudete, pretože svoje moduly a funkcie budete môcť jednoducho testovať na individuálnej úrovni.
Caught a mistake or want to contribute to the lesson? Edit this lesson on GitHub!