Ao lidar com tuplas, temos algumas funções para nos ajudar
Outro exemplo simples:
test/tuple_test.exs
defmodule TupleTest do
use ExUnit.Case
#...
test "inserting elements" do
data = {:initial}
data = Tuple.insert_at(data, 1, :foo)
data = Tuple.insert_at(data, 2, :bar)
assert data == {:initial, :foo, :bar}
assert tuple_size(data) == 3
assert elem(data, 1) == :foo
end
end
mix test test/tuple_test.exs
..
Finished in 0.01 seconds (0.00s async, 0.01s sync)
2 tests, 0 failures
Utilização de tupla de :ok e :error em uma função
Convenções
test/register_test.exs
defmodule VendorTest do
use ExUnit.Case
describe "register/2" do
test "when params are valid" do
id = 54853
description = "Novo ponto de venda"
{:ok, result} = Vendor.register(id, description)
assert result == %{description: "Novo ponto de venda", id: 54853}
end
end
end
Conseguimos extrair o result utilizando pattern matching. O :ok garante que so poderá ter um matching caso venha um :ok no primeiro elemento da tupla.
Se rodarmos isso teremos os primeiros relatórios de erro
mix test test/vendor_test.exs
warning: Vendor.register/2 is undefined (module Vendor is not available or is yet to be defined)
test/vendor_test.exs:8: VendorTest."test register/2"/1
1) test register/2 (VendorTest)
test/vendor_test.exs:4
** (UndefinedFunctionError) function Vendor.register/2 is undefined (module Vendor is not available)
code: {:ok, result} = Vendor.register(id, description)
stacktrace:
Vendor.register(54853, "Novo ponto de venda")
test/vendor_test.exs:8: (test)
Finished in 0.02 seconds (0.00s async, 0.02s sync)
1 test, 1 failure
Precisamos criar nosso módulo com nossa função
lib/vendor.ex
defmodule Vendor do
def register(id, description) do
result = %{id: id, description: description}
{:ok, result} # temos a tupla com o :ok simbolizando que tudo deu certo
end
end
mix test test/vendor_test.exs
Compiling 1 file (.ex)
.
Finished in 0.00 seconds (0.00s async, 0.00s sync)
1 test, 0 failures
defmodule VendorTest do
use ExUnit.Case
describe "register/2" do
# ...
test "when params are not valid" do
id = "invalid-id"
description = "Novo ponto de venda"
{:error, reason} = Vendor.register(id, description)
assert reason == "Params are not valid"
end
end
end
Se rodarmos isso veremos que ele gera um novo erro para nós
mix test test/vendor_test.exs
Compiling 1 file (.ex)
.
1) test register/2 when params are not valid (VendorTest)
test/vendor_test.exs:14
** (FunctionClauseError) no function clause matching in Vendor.register/2
The following arguments were given to Vendor.register/2:
# 1
"invalid-id"
# 2
"Novo ponto de venda"
Attempted function clauses (showing 1 out of 1):
def register(id, description) when is_integer(id)
code: {:error, reason} = Vendor.register(id, description)
stacktrace:
(hello_world 0.1.0) lib/vendor.ex:2: Vendor.register/2
test/vendor_test.exs:18: (test)
Finished in 0.01 seconds (0.00s async, 0.01s sync)
2 tests, 1 failure
Isso significa que nao foi encontrado uma função que atenda as espectativas dessa chamada. Devido a precisar ser inteiro e estarmos enviando String o código quebra.
Para resolver esse problema, precisamos criar uma nova função de mesmo nome que atenda a nova especificação. No nosso caso, criaremos uma apenas para informar que algo esta errado e retornar o :error.
lib/vendor.ex
defmodule Vendor do
# ...
def register(_id, _description) do
{:error, "Params are not valid"} # temos a tupla com :error, algo deu errado
end
end
Basta rodar e ver que agora temos o tratamento correto.
mix test test/vendor_test.exs
Compiling 1 file (.ex)
..
Finished in 0.01 seconds (0.00s async, 0.01s sync)
2 tests, 0 failures
Vamos supor que faremos uma operação de cadastro em um fornecedor. Após a operação precisamos informar se a operação foi bem sucedida ou não. Para facilitar chamarei esse modulo de Vendor e a função de register. No registro precisamos apenas de um ID e uam descrição. Então passaremos dois parâmetros para a função. Queremos como retorno, a utilização de tuplas com :ok e :error chamado de (ou, tratamento de erro).