DEV Community

Joseph Lozano
Joseph Lozano

Posted on • Originally published at joseph.engineering

Testing With Elixir Part 0

This is the first of what I hope will be many posts on testing in Elixir.

This is part 0, and not Elixir-specific. Just the basics of testing.

Given, When, Then

Every (unit) test should be able to be broken down into these parts.

  1. Given some context or situation
  2. When I invoke the function I am trying to test
  3. Then I get the expected result

Let's take a trivial example. I am writing a function to capitalize every other letter of a string.

def spongebob_case(string) do
 # ???
end
Enter fullscreen mode Exit fullscreen mode

Our given is the various cases we are trying to put under test.

cases = [
  "lorem ipsum dolor sit amet, consectetur adipiscing elit",
  "sed do eiusmod tempor incididunt ut labore et dolore magna aliqua",
  "Ut enim ad minim veniam",
  "quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat"
]
Enter fullscreen mode Exit fullscreen mode

Our when is invoking the function with each case. We store this into a variable called actual.

actual = Enum.map(cases, fn kase ->
  spongebob_case(kase)
end)
Enter fullscreen mode Exit fullscreen mode

Our then is making the assertion against our expected result

expected = [
  "lOrEm iPsUm dOlOr sIt aMeT, cOnSeCtEtUr aDiPiScInG ElIt",
  "sEd dO EiUsMoD TeMpOr iNcIdIdUnT Ut lAbOrE Et dOlOrE MaGnA AlIqUa"
  "uT EnIm aD MiNiM VeNiAm",
  "qUiS NoStRuD ExErCiTaTiOn uLlAmCo lAbOrIs nIsI Ut aLiQuIp eX Ea cOmMoDo cOnSeQuAt"
]

assert actual == expected
Enter fullscreen mode Exit fullscreen mode

The full test looks like this:

defmodule SpongebobTest do
  use ExUnit.Case
  import SpongeBob, only: [spongebob_case: 1]

  test "spongebob_case/1 makes every letter other letter upper case" do
    cases = [
      "lorem ipsum dolor sit amet, consectetur adipiscing elit",
      "sed do eiusmod tempor incididunt ut labore et dolore magna aliqua",
      "Ut enim ad minim veniam",
      "quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat"
    ]

    actual =
      Enum.map(cases, fn kase ->
        spongebob_case(kase)
      end)

    assert actual == [
             "lOrEm iPsUm dOlOr sIt aMeT, cOnSeCtEtUr aDiPiScInG ElIt",
             "sEd dO EiUsMoD TeMpOr iNcIdIdUnT Ut lAbOrE Et dOlOrE MaGnA AlIqUa",
             "uT EnIm aD MiNiM VeNiAm",
             "qUiS NoStRuD ExErCiTaTiOn uLlAmCo lAbOrIs nIsI Ut aLiQuIp eX Ea cOmMoDo cOnSeQuAt"
           ]
  end
end
Enter fullscreen mode Exit fullscreen mode

Generally we want to start with 1 simple case, then add more cases, (including edge-cases) as we build out the actual implementation.

Edge cases in this example would be:

  1. What if there is an integer in the input? an emoji? You can't upper case those.
  2. What if the input isn't a string?

Exactly how you handle those edge-cases is usually a product question. As the engineer writing tests, however, it is important that you think about them, and make sure that they are under test.

In future parts, we will talk about how to test GenServers/Processes. Dynamically generating tests, as well as looking at other assertions.

Thanks for reading.

Discussion (0)