Fork me on GitHub

Testando

This translation is up to date.

Testes são uma parte importante do desenvolvimento de software. Nesta lição nós veremos como testar nosso código Elixir com ExUnit e algumas das melhores práticas de como fazer isto.

Sumário

ExUnit

O framework de testes integrado do Elixir é o ExUnit, isto inclui tudo o que precisamos para testar exaustivamente o nosso código. Antes de avançar, é importante notar que testes são implementados como scripts Elixir, por isso precisamos usar a extensão de arquivo .exs. Antes de podermos executar nossos testes nós precisamos iniciar o ExUnit com ExUnit.start(), este é mais comumente feito em test/test_helper.exs.

Quando geramos nosso projeto de exemplo na lição anterior, o mix foi útil o suficiente para criar um teste simples para nós, podemos encontrá-lo em test/example_test.exs:

defmodule ExampleTest do
  use ExUnit.Case
  doctest Example

  test "greets the world" do
    assert Example.hello() == :world
  end
end

Podemos executar testes do nosso projeto com mix test. Se fizermos isso agora devemos ver uma saída semelhante a:

..

Finished in 0.03 seconds
2 tests, 0 failures

Porque há dois testes na saída? Vamos dar uma olhada em lib/example.ex. Mix criou outro teste lá para nós, algum doctest.

defmodule Example do
  @moduledoc """
  Documentation for Example.
  """

  @doc """
  Hello world.

  ## Examples

      iex> Example.hello
      :world

  """
  def hello do
    :world
  end
end

assert

Se você escreveu testes antes, então você está familiarizado com assert; em alguns frameworks should ou expect preenchem o papel de assert.

Usamos o assert macro para testar se a expressão é verdadeira. No caso em que não é, um erro vai ser levantado e os nossos testes irão falhar. Para testar uma falha vamos mudar nosso exemplo e em seguida, executar o mix test.

defmodule ExampleTest do
  use ExUnit.Case
  doctest Example

  test "greets the world" do
    assert Example.hello() == :word
  end
end

Agora nós devemos ver uma saída bem diferente:

  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 nos diz exatamente onde nossos asserts falharam, qual era o valor esperado e qual o valor atual.

refute

refute é para assert como unless é para if. Use refute quando você quiser garantir que uma declaração é sempre falsa.

assert_raise

Às vezes pode ser necessário afirmar que um erro foi levantado, podemos fazer isso com assert_raise. Vamos ver um exemplo de assert_raise na próxima lição sobre Plug.

assert_receive

Em Elixir, as aplicações consistem em atores/processos que enviam mensagens um para o outro, portanto muitas vezes você quer testar mensagens sendo enviada. Como o ExUnit executa seu próprio processo ele pode receber mensagem como qualquer outro processo e você pode afirmar isso com a macro 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 não espera mesangens com assert_receive você pode especificar um tempo limite.

capture_io and capture_log

Capturar uma saída da aplicação é possível com ExUnit.CaptureIO sem mudar a aplicação original. Basta passar a função gerando a saída em:

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 é o equivalente para capturar a saída de Logger.

Configuração de Teste

Em alguns casos, pode ser necessária a realização de configuração antes de nossos testes. Para fazer isso acontecer, nós podemos usar as macros setup e setup_all. setup irá ser executado antes de cada teste, e setup_all uma vez antes da suite de testes. Espera-se que eles vão retornar uma tupla de {:ok, state}, o estado estará disponível para os nossos testes.

Por uma questão de exemplo, vamos mudar o nosso código para usar 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

Mocking

A resposta simples para mocking no Elixir é: não faça isso. Você pode instintivamente querer utilizar mocks, porém eles são altamente desaconselhados na comunidade Elixir por uma boa razão.

Para uma discussão mais longa, temos este excelente artigo. O ponto principal é que ao invés de mockar dependências para testar (mock como verbo), existem muitas vantagens em explicitamente definir interfaces (comportamentos) para código fora da aplicação e usar implementações mockadas (mock como nome) no nosso código cliente para testar.

Para alternar entre as implementações no código da aplicação, a maneira preferida é passar o módulo como argumento e usar um valor padrão. Se isto não funcionar, use o mecanismo de configuração embutido. Para criar estas implementações mockadas, você não precisa de uma biblioteca especial de mocking, apenas comportamentos e callbacks.


Contributors

loading...



Compartilhe essa página