TDD
Elixir
ElixirDay 9

ドキュメントトリガーテスト駆動 Elixir ( Document Trigger Test Driven Elixir ) #elixir #tdd

More than 1 year has passed since last update.

ドキュメントトリガーテスト駆動 Elixir ( Document Trigger Test Driven Elixir )

概要

Elixir でドキュメントの Example を元にテスト駆動開発をします

前提

Elixri の Doctests を利用します。
詳しくは下記記事を参照
Elixir | Doctests

仕様

FizzBuzzプログラムを作成します

  • インターフェースは Fizzbuzz.fizzbuzz(from, to)
  • from, to ともに Integer のみ許容
    • Integer 以外を指定した場合は、RuntimeError を投げる
  • from から to までの FizzBuzz の結果を配列として返却
  • 15 の倍数は "FizzBuzz"
  • 3 の倍数は "Fizz"
  • 5 の倍数は "Buzz"
  • 3/5/15 の倍数以外は 入力数値をそのまま返却

手順

プロジェクトの作成

mix コマンドでプロジェクトテンプレートを生成します。
今回は

  • lib/fizzbuzz.ex
  • test/fizzbuzz_test.ex

のみを編集します。

$ mix new fizzbuzz
* creating README.md
* creating .gitignore
* creating mix.exs
* creating config
* creating config/config.exs
* creating lib
* creating lib/fizzbuzz.ex
* creating test
* creating test/test_helper.exs
* creating test/fizzbuzz_test.exs

テストを実行

自動生成直後は、空のプロダクトコードと成功するテスト Example が実装されているため、
1件テストをパスします。

$ mix test
Compiled lib/fizzbuzz.ex
Generated fizzbuzz.app
.

Finished in 0.04 seconds (0.04s on load, 0.00s on tests)
1 tests, 0 failures

テストコード:Example をテストするために doctest を呼び出します。

これ以降テストコードは一切変更しません

  • test/fizzbuzz_test.exs
defmodule FizzbuzzTest do
  use ExUnit.Case
  doctest Fizzbuzz
end
  • テストを実行。結果は 0 件
$ mix test --trace
FizzbuzzTest


Finished in 0.04 seconds (0.04s on load, 0.00s on tests)
0 tests, 0 failures

プロダクトコード:数値をそのまま返却する Example を追加

  • Example を追加
defmodule Fizzbuzz do
  @doc ~S"""
  FizzBuzz.

  ## Examples

      # only other numbers
      iex> Fizzbuzz.fizzbuzz(1,2)
      [1, 2]
  """
  def fizzbuzz(from, to) do
  end
end
  • テストを実行 未実装のためテストに失敗します
$ mix test --trace
lib/fizzbuzz.ex:11: warning: variable from is unused
lib/fizzbuzz.ex:11: warning: variable to is unused
Compiled lib/fizzbuzz.ex
Generated fizzbuzz.app

FizzbuzzTest
  * doc at Fizzbuzz.fizzbuzz/2 (1) (0.00ms)

  1) test doc at Fizzbuzz.fizzbuzz/2 (1) (FizzbuzzTest)
     test/fizzbuzz_test.exs:3
     Doctest failed
     code: Fizzbuzz.fizzbuzz(1,2) === [1, 2]
     lhs:  nil
     stacktrace:
       lib/fizzbuzz.ex:11: Fizzbuzz (module)



Finished in 0.07 seconds (0.07s on load, 0.00s on tests)
1 tests, 1 failures

Randomized with seed 926333

プロダクトコード:数値をそのまま返却する Example を実装

  • lib/fizzbuzz.ex
defmodule Fizzbuzz do
  @doc ~S"""
  FizzBuzz.

  ## Examples

      # only other numbers
      iex> Fizzbuzz.fizzbuzz(1,2)
      [1, 2]
  """
  def fizzbuzz(from, to) do
    from..to |> Enum.map(&(&1))
  end
end
  • テストを実行 テストをパスしました
$ mix test --trace
Compiled lib/fizzbuzz.ex
Generated fizzbuzz.app

FizzbuzzTest
  * doc at Fizzbuzz.fizzbuzz/2 (1) (5.7ms)


Finished in 0.07 seconds (0.07s on load, 0.00s on tests)
1 tests, 0 failures

プロダクトコード:Fizz の Example を追加

  • lib/fizzbuzz.exs
defmodule Fizzbuzz do
  @doc ~S"""
  FizzBuzz.

  ## Examples

      # only other numbers
      iex> Fizzbuzz.fizzbuzz(1,2)
      [1, 2]

      # only Fizz
      iex> Fizzbuzz.fizzbuzz(3, 3)
      ["Fizz"]
      iex> Fizzbuzz.fizzbuzz(6, 6)
      ["Fizz"]
  """
  def fizzbuzz(from, to) do
    from..to |> Enum.map(&(&1))
  end
end
  • テストを実行 未実装のためテストに失敗します
$ mix test --trace
Compiled lib/fizzbuzz.ex
Generated fizzbuzz.app

FizzbuzzTest
  * doc at Fizzbuzz.fizzbuzz/2 (1) (4.9ms)
  * doc at Fizzbuzz.fizzbuzz/2 (2) (0.02ms)

  1) test doc at Fizzbuzz.fizzbuzz/2 (2) (FizzbuzzTest)
     test/fizzbuzz_test.exs:3
     Doctest failed
     code: Fizzbuzz.fizzbuzz(3, 3) === ["Fizz"]
     lhs:  [3]
     stacktrace:
       lib/fizzbuzz.ex:18: Fizzbuzz (module)



Finished in 0.07 seconds (0.07s on load, 0.00s on tests)
2 tests, 1 failures

Randomized with seed 118398

プロダクトコード:Fizz を実装

  • lib/fizzbuzz.ex
defmodule Fizzbuzz do
  @doc ~S"""
  FizzBuzz.

  ## Examples

      # only other numbers
      iex> Fizzbuzz.fizzbuzz(1,2)
      [1, 2]

      # only Fizz
      iex> Fizzbuzz.fizzbuzz(3, 3)
      ["Fizz"]
      iex> Fizzbuzz.fizzbuzz(6, 6)
      ["Fizz"]
  """
  def fizzbuzz(from, to) do
    from..to |> Enum.map(&(fizzbuzz/1))
  end

  defp fizzbuzz(n) when rem(n, 3) == 0 do
    "Fizz"
  end
  defp fizzbuzz(n) do
    n
  end
end
  • テストを実行 テストをパスしました
$ mix test --trace
Compiled lib/fizzbuzz.ex
Generated fizzbuzz.app

FizzbuzzTest
  * doc at Fizzbuzz.fizzbuzz/2 (2) (4.4ms)
  * doc at Fizzbuzz.fizzbuzz/2 (1) (0.06ms)


Finished in 0.09 seconds (0.09s on load, 0.00s on tests)
2 tests, 0 failures

Randomized with seed 629312

プロダクトコード:Buzz の Example を追加

  • lib/fizzbuzz.exs
defmodule Fizzbuzz do
  @doc ~S"""
  FizzBuzz.

  ## Examples

      # only other numbers
      iex> Fizzbuzz.fizzbuzz(1,2)
      [1, 2]

      # only Fizz
      iex> Fizzbuzz.fizzbuzz(3, 3)
      ["Fizz"]
      iex> Fizzbuzz.fizzbuzz(6, 6)
      ["Fizz"]

      # only Buzz
      iex> Fizzbuzz.fizzbuzz(5, 5)
      ["Buzz"]
      iex> Fizzbuzz.fizzbuzz(10, 10)
      ["Buzz"]
  """
  def fizzbuzz(from, to) do
    from..to |> Enum.map(&(fizzbuzz/1))
  end

  defp fizzbuzz(n) when rem(n, 3) == 0 do
    "Fizz"
  end

  defp fizzbuzz(n) do
    n
  end
end
  • テストを実行 未実装のためテストに失敗します
$ mix test --trace
Compiled lib/fizzbuzz.ex
Generated fizzbuzz.app

FizzbuzzTest
  * doc at Fizzbuzz.fizzbuzz/2 (2) (4.0ms)
  * doc at Fizzbuzz.fizzbuzz/2 (1) (0.02ms)
  * doc at Fizzbuzz.fizzbuzz/2 (3) (0.06ms)

  1) test doc at Fizzbuzz.fizzbuzz/2 (3) (FizzbuzzTest)
     test/fizzbuzz_test.exs:3
     Doctest failed
     code: Fizzbuzz.fizzbuzz(5, 5) === ["Buzz"]
     lhs:  [5]
     stacktrace:
       lib/fizzbuzz.ex:23: Fizzbuzz (module)



Finished in 0.08 seconds (0.08s on load, 0.00s on tests)
3 tests, 1 failures

Randomized with seed 857202

プロダクトコード:Buzz を実装

  • lib/fizzbuzz.ex
defmodule Fizzbuzz do
  @doc ~S"""
  FizzBuzz.

  ## Examples

      # only other numbers
      iex> Fizzbuzz.fizzbuzz(1,2)
      [1, 2]

      # only Fizz
      iex> Fizzbuzz.fizzbuzz(3, 3)
      ["Fizz"]
      iex> Fizzbuzz.fizzbuzz(6, 6)
      ["Fizz"]

      # only Buzz
      iex> Fizzbuzz.fizzbuzz(5, 5)
      ["Buzz"]
      iex> Fizzbuzz.fizzbuzz(10, 10)
      ["Buzz"]
  """
  def fizzbuzz(from, to) do
    from..to |> Enum.map(&(fizzbuzz/1))
  end

  defp fizzbuzz(n) when rem(n, 3) == 0 do
    "Fizz"
  end

  defp fizzbuzz(n) when rem(n, 5) == 0 do
    "Buzz"
  end

  defp fizzbuzz(n) do
    n
  end
end
  • テストを実行 テストをパスしました
$ mix test --trace
Compiled lib/fizzbuzz.ex
Generated fizzbuzz.app

FizzbuzzTest
  * doc at Fizzbuzz.fizzbuzz/2 (3) (3.9ms)
  * doc at Fizzbuzz.fizzbuzz/2 (1) (0.02ms)
  * doc at Fizzbuzz.fizzbuzz/2 (2) (0.1ms)


Finished in 0.08 seconds (0.08s on load, 0.00s on tests)
3 tests, 0 failures

Randomized with seed 677241

プロダクトコード:FizzBuzz の Example を追加

  • lib/fizzbuzz.exs
defmodule Fizzbuzz do
  @doc ~S"""
  FizzBuzz.

  ## Examples

      # only other numbers
      iex> Fizzbuzz.fizzbuzz(1,2)
      [1, 2]

      # only Fizz
      iex> Fizzbuzz.fizzbuzz(3, 3)
      ["Fizz"]
      iex> Fizzbuzz.fizzbuzz(6, 6)
      ["Fizz"]

      # only Buzz
      iex> Fizzbuzz.fizzbuzz(5, 5)
      ["Buzz"]
      iex> Fizzbuzz.fizzbuzz(10, 10)
      ["Buzz"]

      # only FizzBuzz
      iex> Fizzbuzz.fizzbuzz(15, 15)
      ["FizzBuzz"]
      iex> Fizzbuzz.fizzbuzz(30, 30)
      ["FizzBuzz"]

      # mix case
      iex> Fizzbuzz.fizzbuzz(1, 15)
      [1, 2, "Fizz", 4, "Buzz", "Fizz", 7, 8, "Fizz", "Buzz", 11, "Fizz", 13, 14, "FizzBuzz"]
  """
  def fizzbuzz(from, to) do
    from..to |> Enum.map(&(fizzbuzz/1))
  end

  defp fizzbuzz(n) when rem(n, 3) == 0 do
    "Fizz"
  end

  defp fizzbuzz(n) when rem(n, 5) == 0 do
    "Buzz"
  end

  defp fizzbuzz(n) do
    n
  end
end
  • テストを実行 未実装のためテストに失敗します
$ mix test --trace
Compiled lib/fizzbuzz.ex
Generated fizzbuzz.app

FizzbuzzTest
  * doc at Fizzbuzz.fizzbuzz/2 (2) (4.9ms)
  * doc at Fizzbuzz.fizzbuzz/2 (1) (0.03ms)
  * doc at Fizzbuzz.fizzbuzz/2 (3) (0.05ms)
  * doc at Fizzbuzz.fizzbuzz/2 (5) (0.1ms)

  1) test doc at Fizzbuzz.fizzbuzz/2 (5) (FizzbuzzTest)
     test/fizzbuzz_test.exs:3
     Doctest failed
     code: Fizzbuzz.fizzbuzz(1, 15) === [1, 2, "Fizz", 4, "Buzz", "Fizz", 7, 8, "Fizz", "Buzz", 11, "Fizz", 13, 14, "FizzBuzz"]
     lhs:  [1, 2, "Fizz", 4, "Buzz", "Fizz", 7, 8, "Fizz", "Buzz", 11, "Fizz",
            13, 14, "Fizz"]
     stacktrace:
       lib/fizzbuzz.ex:33: Fizzbuzz (module)

  * doc at Fizzbuzz.fizzbuzz/2 (4) (0.1ms)

  2) test doc at Fizzbuzz.fizzbuzz/2 (4) (FizzbuzzTest)
     test/fizzbuzz_test.exs:3
     Doctest failed
     code: Fizzbuzz.fizzbuzz(15, 15) === ["FizzBuzz"]
     lhs:  ["Fizz"]
     stacktrace:
       lib/fizzbuzz.ex:33: Fizzbuzz (module)



Finished in 0.09 seconds (0.09s on load, 0.00s on tests)
5 tests, 2 failures

Randomized with seed 64741

プロダクトコード:FizzBuzz を実装

  • lib/fizzbuzz.ex
defmodule Fizzbuzz do
  @doc ~S"""
  FizzBuzz.

  ## Examples

      # only other numbers
      iex> Fizzbuzz.fizzbuzz(1,2)
      [1, 2]

      # only Fizz
      iex> Fizzbuzz.fizzbuzz(3, 3)
      ["Fizz"]
      iex> Fizzbuzz.fizzbuzz(6, 6)
      ["Fizz"]

      # only Buzz
      iex> Fizzbuzz.fizzbuzz(5, 5)
      ["Buzz"]
      iex> Fizzbuzz.fizzbuzz(10, 10)
      ["Buzz"]

      # only FizzBuzz
      iex> Fizzbuzz.fizzbuzz(15, 15)
      ["FizzBuzz"]
      iex> Fizzbuzz.fizzbuzz(30, 30)
      ["FizzBuzz"]

      # mix case
      iex> Fizzbuzz.fizzbuzz(1, 15)
      [1, 2, "Fizz", 4, "Buzz", "Fizz", 7, 8, "Fizz", "Buzz", 11, "Fizz", 13, 14, "FizzBuzz"]
  """
  def fizzbuzz(from, to) do
    from..to |> Enum.map(&(fizzbuzz/1))
  end

  defp fizzbuzz(n) when rem(n, 15) == 0 do
    "FizzBuzz"
  end

  defp fizzbuzz(n) when rem(n, 3) == 0 do
    "Fizz"
  end

  defp fizzbuzz(n) when rem(n, 5) == 0 do
    "Buzz"
  end

  defp fizzbuzz(n) do
    n
  end
end
  • テストを実行 テストをパスしました
$ mix test --trace
Compiled lib/fizzbuzz.ex
Generated fizzbuzz.app

FizzbuzzTest
  * doc at Fizzbuzz.fizzbuzz/2 (2) (13.8ms)
  * doc at Fizzbuzz.fizzbuzz/2 (1) (0.01ms)
  * doc at Fizzbuzz.fizzbuzz/2 (4) (0.07ms)
  * doc at Fizzbuzz.fizzbuzz/2 (3) (0.02ms)
  * doc at Fizzbuzz.fizzbuzz/2 (5) (0.01ms)


Finished in 0.1 seconds (0.09s on load, 0.01s on tests)
5 tests, 0 failures

Randomized with seed 768461

プロダクトコード:from / to に不正な引数( Integer 以外)を指定する Example を追加

  • lib/fizzbuzz.exs
defmodule Fizzbuzz do
  @doc ~S"""
  FizzBuzz.

  ## Examples

      # only other numbers
      iex> Fizzbuzz.fizzbuzz(1,2)
      [1, 2]

      # only Fizz
      iex> Fizzbuzz.fizzbuzz(3, 3)
      ["Fizz"]
      iex> Fizzbuzz.fizzbuzz(6, 6)
      ["Fizz"]

      # only Buzz
      iex> Fizzbuzz.fizzbuzz(5, 5)
      ["Buzz"]
      iex> Fizzbuzz.fizzbuzz(10, 10)
      ["Buzz"]

      # only FizzBuzz
      iex> Fizzbuzz.fizzbuzz(15, 15)
      ["FizzBuzz"]
      iex> Fizzbuzz.fizzbuzz(30, 30)
      ["FizzBuzz"]

      # mix case
      iex> Fizzbuzz.fizzbuzz(1, 15)
      [1, 2, "Fizz", 4, "Buzz", "Fizz", 7, 8, "Fizz", "Buzz", 11, "Fizz", 13, 14, "FizzBuzz"]

      # args 'to' not integer
      iex> Fizzbuzz.fizzbuzz("1", 15)
      ** (RuntimeError) invalid argument
          (fizzbuzz) lib/fizzbuzz.ex:37: Fizzbuzz.fizzbuzz/2

      # args 'to' not integer
      iex> Fizzbuzz.fizzbuzz(1, "15")
      ** (RuntimeError) invalid argument
  """
  def fizzbuzz(from, to) do
    from..to |> Enum.map(&(fizzbuzz/1))
  end

  defp fizzbuzz(n) when rem(n, 15) == 0 do
    "FizzBuzz"
  end

  defp fizzbuzz(n) when rem(n, 3) == 0 do
    "Fizz"
  end

  defp fizzbuzz(n) when rem(n, 5) == 0 do
    "Buzz"
  end

  defp fizzbuzz(n) do
    n
  end
end
  • テストを実行 未実装のためテストに失敗します
$ mix test --trace
warning: the VM is running with native name encoding of latin1 which may cause Elixir to malfunction as it expects utf8. Please ensure your locale is set to UTF-8 (which can be verified by running "locale" in your shell)
Compiled lib/fizzbuzz.ex
Generated fizzbuzz.app

FizzbuzzTest
  * doc at Fizzbuzz.fizzbuzz/2 (6) (10.5ms)

  1) test doc at Fizzbuzz.fizzbuzz/2 (6) (FizzbuzzTest)
     test/fizzbuzz_test.exs:3
     Doctest failed: expected exception RuntimeError with message "invalid argument" but got Protocol.UndefinedError with message "protocol Range.Iterator not implemented for \"1\""
     code: Fizzbuzz.fizzbuzz("1", 15)
     stacktrace:
       lib/fizzbuzz.ex:42: Fizzbuzz (module)

  * doc at Fizzbuzz.fizzbuzz/2 (1) (1.4ms)
  * doc at Fizzbuzz.fizzbuzz/2 (4) (1.8ms)
  * doc at Fizzbuzz.fizzbuzz/2 (5) (1.2ms)
  * doc at Fizzbuzz.fizzbuzz/2 (2) (0.07ms)
  * doc at Fizzbuzz.fizzbuzz/2 (7) (6.9ms)

  2) test doc at Fizzbuzz.fizzbuzz/2 (7) (FizzbuzzTest)
     test/fizzbuzz_test.exs:3
     Doctest failed: expected exception RuntimeError with message "invalid argument" but got FunctionClauseError with message "no function clause matching in Range.Iterator.Integer.next/2"
     code: Fizzbuzz.fizzbuzz(1, "15")
     stacktrace:
       lib/fizzbuzz.ex:42: Fizzbuzz (module)

  * doc at Fizzbuzz.fizzbuzz/2 (3) (0.2ms)


Finished in 0.1 seconds (0.1s on load, 0.02s on tests)
7 tests, 2 failures

Randomized with seed 32922

プロダクトコード:from / to に不正な引数( Integer 以外)を指定した場合に、RuntimeError を投げるように実装

  • lib/fizzbuzz.ex
defmodule Fizzbuzz do
  @doc ~S"""
  FizzBuzz.

  ## Examples

      # only other numbers
      iex> Fizzbuzz.fizzbuzz(1,2)
      [1, 2]

      # only Fizz
      iex> Fizzbuzz.fizzbuzz(3, 3)
      ["Fizz"]
      iex> Fizzbuzz.fizzbuzz(6, 6)
      ["Fizz"]

      # only Buzz
      iex> Fizzbuzz.fizzbuzz(5, 5)
      ["Buzz"]
      iex> Fizzbuzz.fizzbuzz(10, 10)
      ["Buzz"]

      # only FizzBuzz
      iex> Fizzbuzz.fizzbuzz(15, 15)
      ["FizzBuzz"]
      iex> Fizzbuzz.fizzbuzz(30, 30)
      ["FizzBuzz"]

      # mix case
      iex> Fizzbuzz.fizzbuzz(1, 15)
      [1, 2, "Fizz", 4, "Buzz", "Fizz", 7, 8, "Fizz", "Buzz", 11, "Fizz", 13, 14, "FizzBuzz"]

      # args 'to' not integer
      iex> Fizzbuzz.fizzbuzz("1", 15)
      ** (RuntimeError) invalid argument
          (fizzbuzz) lib/fizzbuzz.ex:37: Fizzbuzz.fizzbuzz/2

      # args 'to' not integer
      iex> Fizzbuzz.fizzbuzz(1, "15")
      ** (RuntimeError) invalid argument
  """
  def fizzbuzz(from, to) when is_integer(from) and is_integer(to) do
    from..to |> Enum.map(&(fizzbuzz/1))
  end

  def fizzbuzz(_from, _to) do
    raise "invalid argument"
  end

  defp fizzbuzz(n) when rem(n, 15) == 0 do
    "FizzBuzz"
  end

  defp fizzbuzz(n) when rem(n, 3) == 0 do
    "Fizz"
  end

  defp fizzbuzz(n) when rem(n, 5) == 0 do
    "Buzz"
  end

  defp fizzbuzz(n) do
    n
  end
end
  • テストを実行 テストをパスしました
$ mix test --trace
Compiled lib/fizzbuzz.ex
Generated fizzbuzz.app

FizzbuzzTest
  * doc at Fizzbuzz.fizzbuzz/2 (6) (0.9ms)
  * doc at Fizzbuzz.fizzbuzz/2 (1) (3.8ms)
  * doc at Fizzbuzz.fizzbuzz/2 (4) (0.03ms)
  * doc at Fizzbuzz.fizzbuzz/2 (7) (0.04ms)
  * doc at Fizzbuzz.fizzbuzz/2 (2) (0.03ms)
  * doc at Fizzbuzz.fizzbuzz/2 (3) (0.05ms)
  * doc at Fizzbuzz.fizzbuzz/2 (5) (0.02ms)


Finished in 0.1 seconds (0.1s on load, 0.00s on tests)
7 tests, 0 failures

Randomized with seed 951920

プロダクトコード:リファクタリング

各プライベート関数の処理が短いので1行で記述するように修正します

  • lib/fizzbuzz.ex
defmodule Fizzbuzz do
  @doc ~S"""
  FizzBuzz.

  ## Examples

      # only other numbers
      iex> Fizzbuzz.fizzbuzz(1,2)
      [1, 2]

      # only Fizz
      iex> Fizzbuzz.fizzbuzz(3, 3)
      ["Fizz"]
      iex> Fizzbuzz.fizzbuzz(6, 6)
      ["Fizz"]

      # only Buzz
      iex> Fizzbuzz.fizzbuzz(5, 5)
      ["Buzz"]
      iex> Fizzbuzz.fizzbuzz(10, 10)
      ["Buzz"]

      # only FizzBuzz
      iex> Fizzbuzz.fizzbuzz(15, 15)
      ["FizzBuzz"]
      iex> Fizzbuzz.fizzbuzz(30, 30)
      ["FizzBuzz"]

      # mix case
      iex> Fizzbuzz.fizzbuzz(1, 15)
      [1, 2, "Fizz", 4, "Buzz", "Fizz", 7, 8, "Fizz", "Buzz", 11, "Fizz", 13, 14, "FizzBuzz"]

      # args 'to' not integer
      iex> Fizzbuzz.fizzbuzz("1", 15)
      ** (RuntimeError) invalid argument
          (fizzbuzz) lib/fizzbuzz.ex:37: Fizzbuzz.fizzbuzz/2

      # args 'to' not integer
      iex> Fizzbuzz.fizzbuzz(1, "15")
      ** (RuntimeError) invalid argument
  """
  def fizzbuzz(from, to) when is_integer(from) and is_integer(to) do
    from..to |> Enum.map(&(fizzbuzz/1))
  end

  def fizzbuzz(_from, _to), do: raise "invalid argument"
  defp fizzbuzz(num) when rem(num, 15) == 0, do: "FizzBuzz"
  defp fizzbuzz(num) when rem(num, 5) == 0, do: "Buzz"
  defp fizzbuzz(num) when rem(num, 3) == 0, do: "Fizz"
  defp fizzbuzz(num), do: num
end
  • テストを実行 テストをパスしました。 テストを壊さずにリファクタリングに成功しました。
$ mix test --trace
Compiled lib/fizzbuzz.ex
Generated fizzbuzz.app

FizzbuzzTest
  * doc at Fizzbuzz.fizzbuzz/2 (4) (4.8ms)
  * doc at Fizzbuzz.fizzbuzz/2 (7) (1.9ms)
  * doc at Fizzbuzz.fizzbuzz/2 (1) (0.1ms)
  * doc at Fizzbuzz.fizzbuzz/2 (2) (0.1ms)
  * doc at Fizzbuzz.fizzbuzz/2 (5) (0.1ms)
  * doc at Fizzbuzz.fizzbuzz/2 (3) (0.1ms)
  * doc at Fizzbuzz.fizzbuzz/2 (6) (0.08ms)


Finished in 0.1 seconds (0.1s on load, 0.01s on tests)
7 tests, 0 failures

Randomized with seed 216728

完成したプロジェクトを実行

$ mix run -e 'IO.inspect Fizzbuzz.fizzbuzz(1, 30)'
[1, 2, "Fizz", 4, "Buzz", "Fizz", 7, 8, "Fizz", "Buzz", 11, "Fizz", 13, 14,
 "FizzBuzz", 16, 17, "Fizz", 19, "Buzz", "Fizz", 22, 23, "Fizz", "Buzz", 26,
 "Fizz", 28, 29, "FizzBuzz"]

$ mix run -e 'IO.inspect Fizzbuzz.fizzbuzz("1", 30)'
** (RuntimeError) invalid argument
    (fizzbuzz) lib/fizzbuzz.ex:46: Fizzbuzz.fizzbuzz/2
    (stdlib) erl_eval.erl:657: :erl_eval.do_apply/6
    (stdlib) erl_eval.erl:865: :erl_eval.expr_list/6
    (stdlib) erl_eval.erl:407: :erl_eval.expr/5
    (elixir) lib/code.ex:140: Code.eval_string/3
    (elixir) lib/enum.ex:537: Enum."-each/2-lists^foreach/1-0-"/2

$ mix run -e 'IO.inspect Fizzbuzz.fizzbuzz(1, "30")'
** (RuntimeError) invalid argument
    (fizzbuzz) lib/fizzbuzz.ex:46: Fizzbuzz.fizzbuzz/2
    (stdlib) erl_eval.erl:657: :erl_eval.do_apply/6
    (stdlib) erl_eval.erl:865: :erl_eval.expr_list/6
    (stdlib) erl_eval.erl:407: :erl_eval.expr/5
    (elixir) lib/code.ex:140: Code.eval_string/3
    (elixir) lib/enum.ex:537: Enum."-each/2-lists^foreach/1-0-"/2

$ mix run -e 'IO.inspect Fizzbuzz.fizzbuzz(15, 1)'
["FizzBuzz", 14, 13, "Fizz", 11, "Buzz", "Fizz", 8, 7, "Fizz", "Buzz", 4, "Fizz", 2, 1]

Complete!

明日は @keithseahus さんです。