在 Elixir 軌道上測試

了解如何在 Exercism 上測試你的 Elixir 練習


從終端機切換到練習的基底目錄,然後用以下指令執行測試:

$ mix test

這會執行 test 子資料夾裡的測試檔,也就是以 _test.exs 結尾的檔案。

待處理的測試

在練習題的測試套件中,除了第一個測試之外,其餘的測試都被標記為略過。

當你讓一個測試通過後,只要用 # 符號把相關的 @tag :pending 註解掉,就能取消略過下一個測試。

例如:

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

如果你想一次執行所有測試,可以在 mix test 指令加上 --include 參數,把所有被略過的測試都納入:

$ mix test --include pending

或者,你也可以把測試套件裡的 ExUnit.configure 那一行註解掉,來啟用所有測試。

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

其他 Elixir 測試功能

ExUnit 和 mix test 提供了不少分組、標記與執行測試的方法,也有各種控制測試執行方式的做法,以下大致整理了其中一部分。

執行特定測試的方法

文件:

執行特定檔案中的測試

只要指定檔案,就能用 mix test 執行單一檔案裡的所有測試:

$ mix test test/<FILE>.exs

注意:使用這種方式時,tagging 可能會影響實際執行了哪些測試。

執行個別測試

個別測試可以透過指定測試在檔案中的行號來執行:

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

可以用 : 分隔多個行號,來執行多個測試。

例如,假設有個檔案內容如下,並標上行號:

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

第 1 個和第 3 個測試可以這樣執行:

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

注意:透過行號指定測試時,tagging 會被忽略。

執行測試群組

測試可以用 describe 分組:

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

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

群組中的所有測試都能透過指定它在檔案中的行號來執行,就像指定並執行個別測試一樣。

文件:

其他實用的 mix test 選項

  • --include 和 --exclude - 根據測試的 @tag 決定要不要執行特定測試
  • --failed - 只執行上次執行時失敗的測試
  • --max-failures - 當測試失敗數達到這個數字時,測試套件就會停止評估測試
  • --seed - 為用來隨機排序測試的隨機數產生器設定種子 --seed 0 會停用隨機化,這樣單一檔案裡的測試就會永遠按照定義的順序執行
  • --stale - 只執行那些參照到自從上次用 --stale 執行測試後有變動的模組的測試
  • --only task_id:1 - 或是換成其他數字;在學習練習中,只執行與特定任務相關的測試

文件:

型別規格與 Dialyzer(DIscrepancy AnalYZer for ERlang programs)

Elixir 練習的 lib 子目錄裡會有一份骨架實作檔。這個檔案大致列出了你預期要實作的模組與函式。在大多數練習中,你會在函式宣告上方看到型別規格。這些規格以 @spec 標籤開頭,通常遵循 @spec function_name(type1, type2) :: return_type 的格式。這些在 Elixir 和 Erlang 中作為文件使用,也會搭配一個叫做 Dialyzer 的工具,用來找出型別不一致和可能的 bug。想了解更多資訊,請參閱型別規格文件。關於 Dialyzer 的文件,請參閱 Erlang -- dialyzer。

你也可以選擇用 Dialyzer 來檢查實作的型別。這需要幾個步驟。為此,你必須把 Dialyxir 相依套件加到你這個題目的 mix.exs 檔案中。

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

接著用 mix 任務從命令列取得並編譯:

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

如果你是第一次執行 Dialyzer,很可能還沒有 plt 檔案。持久查詢表,也就是 PLT,是 Dialyzer 用來快取 Elixir 和 Erlang 內建型別資訊的。要建立一份有合理預設值的 plt,請執行:

$ mix dialyzer --plt

最後,可以用以下指令執行:

$ mix dialyzer

請務必把路徑改成你系統上 Elixir 函式庫的路徑。例如,如果你用 homebrew 安裝 Elixir,大概會放在 /usr/local/Cellar/elixir/1.3.2。

要再次強調的是,完成練習時,執行 Dialyzer 並移除所有警告是選擇性的步驟。Dialyzer 的警告可能很難解讀。舉例來說,看看這個 Bob 練習的荒唐實作所產生的警告。

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

  def hey(input) do
  end
end

這會產生以下警告。

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()

第一個警告表示這個函式沒有回傳正確的型別。最後一個警告表示第二個函式定義永遠不會被執行到,因為第一個函式定義總是符合。