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!