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

# ドキュメントトリガーテスト駆動 Elixir ( Document Trigger Test Driven Elixir )
## <i class="fa fa-cubes" style="font-size:1em;"></i> 概要
Elixir でドキュメントの Example を元にテスト駆動開発をします
## <i class="fa fa-cube" style="font-size:1em;"></i> 前提
Elixri の Doctests を利用します。
詳しくは下記記事を参照
<i class="fa fa-external-link-square" style="font-size:1em;"></i> [Elixir | Doctests](http://qiita.com/tbpgr/items/2ea8a20c93eeb6959c80)

## <i class="fa fa-file-o" style="font-size:1em;"></i> 仕様
FizzBuzzプログラムを作成します

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

## <i class="fa fa-files-o" style="font-size:1em;"></i> 手順
### <i class="fa fa-plus-square" style="font-size:1em;"></i> プロジェクトの作成
`mix` コマンドでプロジェクトテンプレートを生成します。
今回は

* lib/fizzbuzz.ex
* test/fizzbuzz_test.ex

のみを編集します。

~~~bash
$ 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
~~~

### <i class="fa fa-play" style="font-size:1em;"></i> テストを実行

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

~~~bash
$ 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
~~~

### <i class="fa fa-terminal" style="font-size:1em;"></i> テストコード：Example をテストするために doctest を呼び出します。
これ以降テストコードは一切変更しません

* test/fizzbuzz_test.exs

~~~elixir
defmodule FizzbuzzTest do
  use ExUnit.Case
  doctest Fizzbuzz
end
~~~

* テストを実行。結果は 0 件

~~~bash
$ mix test --trace
FizzbuzzTest


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

### <i class="fa fa-terminal" style="font-size:1em;"></i> プロダクトコード：数値をそのまま返却する Example を追加

* Example を追加

~~~elixir
defmodule Fizzbuzz do
  @doc ~S"""
  FizzBuzz.

  ## Examples

      # only other numbers
      iex> Fizzbuzz.fizzbuzz(1,2)
      [1, 2]
  """
  def fizzbuzz(from, to) do
  end
end
~~~

* テストを実行
未実装のためテストに失敗します

~~~bash
$ 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
~~~

### <i class="fa fa-terminal" style="font-size:1em;"></i> プロダクトコード：数値をそのまま返却する Example を実装

* lib/fizzbuzz.ex

~~~elixir
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
~~~

* テストを実行
テストをパスしました

~~~bash
$ 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
~~~

### <i class="fa fa-terminal" style="font-size:1em;"></i> プロダクトコード：Fizz の Example を追加

* lib/fizzbuzz.exs

~~~elixir
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
~~~

* テストを実行
未実装のためテストに失敗します

~~~bash
$ 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
~~~

### <i class="fa fa-terminal" style="font-size:1em;"></i>プロダクトコード：Fizz を実装

* lib/fizzbuzz.ex

~~~elixir
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
~~~

* テストを実行
テストをパスしました

~~~bash
$ 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
~~~

### <i class="fa fa-terminal" style="font-size:1em;"></i>プロダクトコード：Buzz の Example を追加

* lib/fizzbuzz.exs

~~~elixir
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
~~~

* テストを実行
未実装のためテストに失敗します

~~~bash
$ 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
~~~

### <i class="fa fa-terminal" style="font-size:1em;"></i> プロダクトコード：Buzz を実装

* lib/fizzbuzz.ex

~~~elixir
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
~~~

* テストを実行
テストをパスしました

~~~bash
$ 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
~~~

### <i class="fa fa-terminal" style="font-size:1em;"></i> プロダクトコード：FizzBuzz の Example を追加

* lib/fizzbuzz.exs

~~~elixir
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
~~~

* テストを実行
未実装のためテストに失敗します

~~~bash
$ 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
~~~

### <i class="fa fa-terminal" style="font-size:1em;"></i> プロダクトコード：FizzBuzz を実装

* lib/fizzbuzz.ex

~~~elixir
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
~~~

* テストを実行
テストをパスしました

~~~bash
$ 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
~~~

### <i class="fa fa-terminal" style="font-size:1em;"></i> プロダクトコード：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
~~~

* テストを実行
未実装のためテストに失敗します

~~~bash
$ 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
~~~

### <i class="fa fa-terminal" style="font-size:1em;"></i> プロダクトコード：from / to に不正な引数( Integer 以外)を指定した場合に、RuntimeError を投げるように実装

* lib/fizzbuzz.ex

~~~elixir
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
~~~

* テストを実行
テストをパスしました

~~~bash
$ 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
~~~

### <i class="fa fa-repeat" style="font-size:1em;"></i> プロダクトコード：リファクタリング
各プライベート関数の処理が短いので1行で記述するように修正します

* lib/fizzbuzz.ex

~~~elixir
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
~~~

* テストを実行
テストをパスしました。
テストを壊さずにリファクタリングに成功しました。

~~~bash
$ 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
~~~

### <i class="fa fa-play" style="font-size:1em;"></i> 完成したプロジェクトを実行

~~~bash
$ 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]
~~~

## <i class="fa fa-trophy" style="font-size:1em;"></i> Complete!

明日は @keithseahus さんです。