Funções nomeadas

A função é o principal elemento das linguagens funcionais (grande surpresa an?) e tem como objetivo realizar comportamentos.

Aridade De forma simples, a aridade é a quantidade de argumentos que uma função recebe.

Uma função login que recebe user e password por parâmetro tem aridade 2 e é representado com o nome da função, barra (/) e o número da aridade. Exemplo: login(user, password) -> login/2 Veja mais no capítulo de Aridade

Funções nomeadas devem existir somente dentro de módulos e são declaradas com def/2 e defp/2 para funções privadas. Como por exemplo:

defmodule Authenticate do
  def login(user, password) do
    # ...
  end
  
  defp create_session(user), do: # ...
end

Vamos criar um novo teste em nosso projeto onde iremos fazer uma soma simples. Na pasta test crie um novo arquivo chamado math_test.exs e nele utilizaremos nosso primeiro describe, descrevendo o que está sendo testado.

Describe

O describe/2 descreve um grupo de testes que tem correlação. Assim podemos criar diversos cenários para uma mesma função que está sendo testada. Exemplo:

  • Quando a função x funciona

  • Quando a função x não funciona devido a parâmetros errados

Normalmente o describe recebe o module + função sendo testada + aridade

Podemos tirar algumas vantagens quando utilizamos describe. Uma delas é servir de documentação. Então vamos pensar. Precisamos fazer uma soma (função) e essa soma irá somar dois valores x e y (parâmetros). Analisando isso, podemos presumir que o contrato de nossa função seja algo assim Math.sum/2. Vamos adicionar isso a nossa describe para facilitar o entendimento

test/math_test.exs
defmodule MathTest do
  use ExUnit.Case

  describe "Math.sum/2" do
    # ...
  end
end

Criaremos o primeiro bloco de teste para quando passarmos os parâmetros corretos.

Parâmetros

Forma de injetar dados dentro de funções de forma a poder ser usada la dentro. A assinatura se da a criação da função usando def/2, definindo o nome da função e em seguida adicionando parênteses. Dentro dos parênteses adicionamos as variáveis posicionais. def nome_da_funcao(parametro_1, parametro_2) Assim você poderá passar dados para dentro da função e os usa-los lá. nome_da_funcao(5, 10)

Vamos adicionar ao código o bloco de teste usando a função test/2

test/math_test.exs
defmodule MathTest do
  use ExUnit.Case

  describe "Math.sum/2" do
    test "when successed return result" do
      
    end
  end
end

Precisamos agora entender o que seria um teste bem sucedido aqui e para isso devemos seguir algumas etapas do TDD para se tornar mais simples

  1. Configurar o teste

  2. Executar a chamada

  3. Criar afirmações

Para configurar o teste, precisamos da função a ser chamada e dos parâmetros que queremos passar, então executamos essa função com os parâmetros e guardados sua resposta, para podermos utilizarmos em nossas afirmações.

Pelo básico, Queremos a soma do valor x = 1 e y = 7 e a resposta deve ser 8. Vamos converter isso para o código

test/math_test.exs
defmodule MathTest do
  use ExUnit.Case

  describe "Math.sum/2" do
    test "when successed response the result" do
      # configurando
      x = 1
      y = 7
      
      # Executando a função
      result = Math.sum(x, y)
      
      # criando afirmações
      assert result == 8
    end
  end
end

Vamos rodar isso e ver o relatório

mix test
warning: Math.sum/2 is undefined (module Math is not available or is yet to be defined)
  test/math_test.exs:11: MathTest."test Math.sum/2 when successed response the result"/1

.

  1) test Math.sum/2 when successed response the result (MathTest)
     test/math_test.exs:5
     ** (UndefinedFunctionError) function Math.sum/2 is undefined (module Math is not available)
     code: result = Math.sum(x, y)
     stacktrace:
       Math.sum(1, 7)
       test/math_test.exs:11: (test)


Finished in 0.03 seconds (0.00s async, 0.03s sync)
2 tests, 1 failure

Possuímos nosso primeiro erro. Decidimos usar TDD e o teste vem antes do código a ser testado, então, não possuímos o código e o teste avisou isso para nós. Math.sum/2 is undefined. Vamos criar o módulo Math com a função sum com 2 de aridade.

TDD

Testing driven design tem como regra guiar nosso código iniciando ele pelos testes. Uma vez que ele falhe temos um ponto de ação a fazer. Sempre focando no menor esforço a se fazer para o teste passar. Leia mais sobre

Crie o novo arquivo e adicione o mínimo necessários para seguirmos em frente.

lib/math.ex
defmodule Math do
  def sum(_x, _y) do

  end
end

Utilizando prefixo underline (_)

Quando temos parâmetros e não o utilizamos, o interpretador nos emite um aviso dizendo que a variável não está sendo utilizada. warning: variable "y" is unused (if the variable is not meant to be used, prefix it with an underscore) lib/math.ex:2: Math.sum/2

Para esse aviso sumir, podemos colocar como prefixo o underline (_) e ele irá ignorar essa validação.

def sum (_x, _y) do

Salve o arquivo e vamos tentar rodar o teste mais uma vez

mix test
Compiling 1 file (.ex)
Generated hello_world app
.

  1) test Math.sum/2 when successed response the result (MathTest)
     test/math_test.exs:5
     Assertion with == failed
     code:  assert result == 8
     left:  nil
     right: 8
     stacktrace:
       test/math_test.exs:14: (test)


Finished in 0.02 seconds (0.00s async, 0.02s sync)
2 tests, 1 failure

Temos um erro diferente (progresso!). O relatório nos conta que o lado esquerdo de nossa assertion está nil, mas o lado direito esperava 8 . Nossa função não esta retornando nada. Podemos ler assim: o resultado da função esta nulo enquanto o esperado no teste é 8.

Agora podemos implementar nosso código real. Vamos fazer a soma e retornar o valor.

lib/math.ex
defmodule Math do
  def sum(x, y) do
    x + y
  end
end

Não precisamos de nenhuma palavra chave para indicar que um valor vai ser retornado na função. Elixir faz isso na última linha da função.

Basta agora rodar o teste e ver tudo funcionar.

mix test
Compiling 1 file (.ex)
..
Finished in 0.01 seconds (0.00s async, 0.01s sync)
2 tests, 0 failures

Conclusão

Vimos o básico de funções. Descobrimos que uma função é definida por seu nome e sua aridade. Que toda função nomeada deve estar dentro de um módulo e que podemos passar dados para dentro dela usando parâmetros posicionais.

Entendemos um pouco mais sobre TDD e criamos nosso primeiro teste do zero.

Last updated