Testar no percurso de Elixir

Aprende a testar os teus exercícios de Elixir no Exercism


No terminal, muda para o diretório base do exercício e executa os testes com:

$ mix test

Isto executa o ficheiro de testes que se encontra na subpasta test, um ficheiro terminado em _test.exs

Testes pendentes

Nas suites de testes dos exercícios de prática, todos os testes exceto o primeiro estão marcados para serem ignorados.

Depois de conseguires passar um teste, podes ativar o próximo comentando o @tag :pending correspondente com o símbolo #.

Por exemplo:

# @tag :pending
test "shouting" do
  assert Bob.hey("WATCH OUT!") == "Whoa, chill out!"
end

Se quiseres executar todos os testes de uma só vez, podes incluir todos os testes ignorados usando a opção --include no comando mix test:

$ mix test --include pending

Ou podes ativar todos os testes comentando a linha ExUnit.configure na suite de testes.

# ExUnit.configure exclude: :pending, trace: true

Funcionalidades adicionais de testes em Elixir

O ExUnit e o mix test oferecem bastantes formas de agrupar, marcar e executar testes, além de vários métodos para controlar a execução dos testes; grande parte disso está resumida abaixo.

Formas de executar testes específicos

Documentação:

Executar testes de um ficheiro específico

Todos os testes de um único ficheiro podem ser executados com mix test, indicando o ficheiro:

$ mix test test/<FILE>.exs

NOTA: o tagging pode afetar quais os testes que são realmente executados com este método.

Executar testes individuais

Os testes individuais podem ser executados referindo o número de linha do teste no ficheiro:

$ mix test test/<FILE>.exs:LINENUM

Podem ser executados vários testes indicando vários números de linha separados por :.

Por exemplo, dado um ficheiro com o seguinte conteúdo com números de linha:

test "Test 1" do           # 1
  # test implementation    # 2-6
end                        # 7
                           # 8
test "Test 2" do           # 9
  # test implementation    # 10-21
end                        # 22
                           # 23
test "Test 3" do           # 24
  # test implementation    # 25-35
end                        # 36

O primeiro e o terceiro testes podem ser executados com:

$ mix test test/FILE.exs:1:24

NOTA: quando indicas testes através de números de linha, o tagging é ignorado.

Executar grupos de testes

Os testes podem ser agrupados com describe:

describe "short test group description" do
  test "test description" do
    # test implementation
  end

  test "another test description" do
    # test implementation
  end
end

Todos os testes de um grupo podem ser executados referindo o número da sua linha no ficheiro, tal como ao referir e executar testes individuais.

Documentação:

Outras opções úteis do mix test

  • --include e --exclude - executam ou não testes específicos com base nos respetivos @tag
  • --failed - executa apenas os testes que falharam na última execução
  • --max-failures - a suite deixa de avaliar testes quando se atinge este número de falhas
  • --seed - define a semente do gerador de números aleatórios usado para baralhar a ordem dos testes --seed 0 desativa a aleatoriedade, para que os testes de um único ficheiro sejam sempre executados pela ordem em que foram definidos
  • --stale - executa apenas os testes que referenciam módulos alterados desde a última vez que os testes foram executados com --stale
  • --only task_id:1 - ou com outro número, nos exercícios de aprendizagem executa apenas os testes associados à tarefa específica

Documentação:

Typespecs e Dialyzer (DIscrepancy AnalYZer for ERlang programs)

Os exercícios de Elixir incluem um ficheiro de implementação esqueleto na subpasta lib. Este ficheiro descreve o módulo e as funções que se espera que implementes. Na maioria dos exercícios, encontras typespecs acima da declaração da função. Estas começam com a etiqueta @spec e seguem habitualmente o formato @spec function_name(type1, type2) :: return_type. São usadas em Elixir e Erlang como documentação e, em conjunto com uma ferramenta chamada Dialyzer, para encontrar inconsistências de tipos e possíveis bugs. Para mais informações, consulta a documentação das typespecs. Para documentação sobre o Dialyzer, consulta Erlang -- dialyzer.

Opcionalmente, podes querer verificar os tipos da tua implementação com o Dialyzer. Há alguns passos que terás de dar. Para isso, tens de adicionar a dependência Dialyxir ao ficheiro mix.exs do teu problema.

defp deps do
  # Add this:
  [{:dialyxir, "~> 0.4", only: [:dev]}]
end

Depois, usa as tarefas do mix para obter e compilar a partir da linha de comandos:

$ mix deps.get
...
$ mix deps.compile
...

Se for a primeira vez que executas o Dialyzer, é muito provável que não tenhas um ficheiro plt. A tabela de consulta persistente, ou PLT, é usada pelo Dialyzer para colocar em cache informação sobre os tipos incorporados de Elixir e Erlang. Para criar um plt com predefinições razoáveis, executa:

$ mix dialyzer --plt

Por fim, podes executá-lo com:

$ mix dialyzer

Certifica-te de que indicas o caminho das bibliotecas de Elixir do teu sistema. Por exemplo, se instalaste o Elixir com o homebrew, provavelmente encontras as bibliotecas em /usr/local/Cellar/elixir/1.3.2.

Vale a pena repetir que executar o Dialyzer e eliminar todos os avisos é um passo opcional ao concluir um exercício. Os avisos do Dialyzer podem ser difíceis de decifrar. Por exemplo, repara nos avisos desta implementação disparatada do exercício Bob.

defmodule Bob do
  @spec hey(input :: String.t()) :: String.t()
  def hey(input) do
    1
  end

  def hey(input) do
  end
end

Isto produz os seguintes avisos.

bob.exs:2: Invalid type specification for function 'Elixir.Bob':hey/1. The success typing is (_) -> 1
bob.exs:7: The variable _input@1 can never match since previous clauses completely covered the type any()

O primeiro aviso significa que a função não está a devolver o tipo correto. O último indica que a segunda definição da função nunca pode ser alcançada, porque a primeira definição da função corresponde sempre.