檢視原始碼 mix test (Mix v1.16.2)

執行專案測試。

此任務啟動目前的應用程式,載入 test/test_helper.exs,然後並行載入所有符合 test/**/*_test.exs 樣式的檔案。

可以在任務名稱後提供檔案和/或目錄清單,以選擇要執行的檔案

$ mix test test/some/particular/file_test.exs
$ mix test test/some/particular/dir

透過指定完整的套件路徑(包括 apps/my_app/test),可以從根目錄執行傘狀專案的測試,這種情況下將完全略過其他子應用程式的遞迴測試

# To run all tests for my_app from the umbrella root
$ mix test apps/my_app/test

# To run a given test file on my_app from the umbrella root
$ mix test apps/my_app/test/some/particular/file_test.exs

了解測試結果

執行測試套件時,它會列印執行中的結果,並在最後提供摘要,如下所示

$ mix test
...

  1) test greets the world (FooTest)
     test/foo_test.exs:5
     Assertion with == failed
     code:  assert Foo.hello() == :world!
     left:  :world
     right: :world!
     stacktrace:
       test/foo_test.exs:6: (test)

........

Finished in 0.05 seconds (0.00s async, 0.05s sync)
1 doctest, 11 tests, 1 failure

Randomized with seed 646219

對於每個測試,測試套件會列印一個點。失敗的測試會立即以下一節中描述的格式列印出來。

執行所有測試後,我們會列印套件摘要。第一行包含花在套件上的總時間,接著是花在非同步測試(使用 use ExUnit.Case, async: true 定義)與同步測試上的時間

Finished in 0.05 seconds (0.00s async, 0.05s sync)

開發人員希望盡可能減少花在同步測試上的時間,因為同步測試會依序執行,而非同步測試會並行執行。

最後,我們執行了多少個測試、其中多少個失敗、多少個無效等等。

了解測試失敗

首先,它包含失敗計數器,接著是測試名稱和定義測試的模組

1) test greets the world (FooTest)

下一行包含 FILE:LINE 格式的測試確切位置

test/foo_test.exs:5

如果你只想重新執行此測試,你只需要複製上面的行,並貼到 mix test 前面即可

$ mix test test/foo_test.exs:5

然後我們會顯示錯誤訊息、程式碼片段和失敗測試的一般資訊

Assertion with == failed
code:  assert Foo.hello() == :world!
left:  :world
right: :world!

如果您的終端機支援著色(請參閱下方的「著色」區段),通常會顯示 leftright 側之間的差異。最後,我們會列印失敗的堆疊追蹤

stacktrace:
  test/foo_test.exs:6: (test)

命令列選項

  • --all-warnings (--no-all-warnings) - 列印所有警告,包括之前的編譯(預設為 true,除非有錯誤)

  • --color - 在輸出中啟用顏色

  • --cover - 執行覆蓋率工具。請參閱下方的「覆蓋率」區段

  • --exclude - 排除符合篩選條件的測試

  • --exit-status - 在測試套件失敗時使用替代的退出狀態(預設為 2)。

  • --export-coverage - 將覆蓋率結果匯出的檔案名稱。僅在與 --cover 搭配使用時有效

  • --failed - 僅執行上次執行時失敗的測試

  • --force - 無論修改時間為何,都強制編譯

  • --formatter - 設定將列印結果的格式化程式模組。預設為 ExUnit 內建的 CLI 格式化程式

  • --include - 包含符合篩選條件的測試

  • --listen-on-stdin - 執行測試,然後在 stdin 上監聽。一旦收到換行符號,它將重新執行測試。請參閱下方的「檔案系統監控程式」區段

  • --max-cases - 設定非同步執行的測試最大數量。只有來自不同模組的測試會並行執行。預設為核心數量的兩倍

  • --max-failures - 當達到此測試失敗數量時,套件會停止評估測試。如果省略,則會執行所有測試

  • --no-archives-check - 不檢查檔案

  • --no-color - 在輸出中停用顏色

  • --no-compile - 不編譯,即使檔案需要編譯

  • --no-deps-check - 不檢查依賴項

  • --no-elixir-version-check - 不檢查 mix.exs 中的 Elixir 版本

  • --no-start - 編譯後不啟動應用程式

  • --only - 僅執行符合篩選條件的測試

  • --partitions - 設定將測試分割成多少個分區。它必須大於零的數字。如果設定為一,它會作為 no-op。如果大於一,則您還必須設定 MIX_TEST_PARTITION 環境變數,其中包含要在目前測試執行中使用的分區。有關更多資訊,請參閱「作業系統程序分區」區段

  • --preload-modules - 預載應用程式中定義的所有模組

  • --profile-require time - 分析載入測試檔案所需時間。僅用於除錯。測試組並不會執行。

  • --raise - 如果測試組失敗,則引發

  • --seed - 隨機數產生器種子,用於隨機化測試順序;--seed 0 停用隨機化,因此單一檔案中的測試將始終按照定義順序執行

  • --slowest - 列印 N 個最慢測試的時間資訊。自動設定 --trace--preload-modules

  • --stale - 僅執行自上次使用 --stale 執行測試以來,參照已變更模組的測試。您可以在下方的「--stale 選項」區段中閱讀更多關於此選項的資訊

  • --timeout - 設定測試逾時

  • --trace - 以詳細報告執行測試。自動將 --max-cases 設定為 1。請注意,在追蹤模式中,測試逾時將被忽略,因為逾時設定為 :infinity

  • --warnings-as-errors - (自 v1.12.0 起) 將警告視為錯誤,並傳回非零結束狀態。此選項僅適用於測試檔案。若要在編譯和測試期間將警告視為錯誤,請執行

    MIX_ENV=test mix do compile --warnings-as-errors + test --warnings-as-errors

設定

這些設定可以在 mix.exsdef project 區段中設定

  • :test_coverage - 傳遞給涵蓋率機制的選項組。請參閱「涵蓋率」區段以取得更多資訊

  • :test_elixirc_options - 載入/編譯測試檔案時使用的編譯器選項。預設停用 debug 區塊和文件區塊

  • :test_paths - 包含測試檔案的路徑清單。如果存在 test 目錄,則預設為 ["test"];否則,預設為 []。預期所有測試路徑都包含 test_helper.exs 檔案

  • :test_pattern - 載入測試檔案的樣式。預設為 *_test.exs

  • :warn_test_pattern - 比對可能命名錯誤的測試檔案並顯示警告的樣式。預設為 *_test.ex

著色

著色在大部分 Unix 終端機上預設啟用。它們也可用於 Windows 10 的 Windows 主控台,但必須在登錄檔中為目前使用者明確啟用,執行下列指令

$ reg add HKCU\Console /v VirtualTerminalLevel /t REG_DWORD /d 1

執行上述指令後,您必須重新啟動目前的主控台。

篩選器

ExUnit 提供標籤和篩選功能,讓開發人員可以選擇要執行的測試。最常見的功能是在測試輔助檔案中預設排除某些特定測試不執行

# Exclude all external tests from running
ExUnit.configure(exclude: [external: true])

然後,只要需要,就可以透過 --include 選項將那些測試包含在執行中

$ mix test --include external:true

上述範例將執行所有將外部選項設定為 true 的測試。也可以包含所有具有特定標籤的範例,不論其值為何

$ mix test --include external

請注意,所有測試在預設情況下都會包含,因此除非它們先被排除(在測試輔助檔案中或透過 --exclude 選項),否則 --include 選項不會生效。

因為這個原因,Mix 也提供一個 --only 選項,用來排除所有測試並只包含指定的測試

$ mix test --only external

這類似於

$ mix test --include external --exclude test

不同之處在於,如果使用 --only 選項時沒有執行任何測試,測試套件將會失敗。

如果正在測試單一檔案,可以傳遞一個或多個特定行號,只執行那些指定的測試

$ mix test test/some/particular/file_test.exs:12

這等同於

$ mix test --exclude test --include line:12 test/some/particular/file_test.exs

$ mix test test/some/particular/file_test.exs:12:24

這等同於

$ mix test --exclude test --include line:12 --include line:24 test/some/particular/file_test.exs

如果某一行開始一個 describe 區塊,該行篩選器將執行其中的所有測試。否則,它會執行在指定行號上或之前最近的測試。

涵蓋率

Elixir 透過 --cover 旗標提供內建的基於行的測試涵蓋率。測試涵蓋率顯示在測試執行期間執行了哪些程式碼行以及哪些檔案。

限制

Elixir 中的覆蓋率有以下限制

  • 字面值(例如原子、字串和數字)不會被覆蓋率追蹤。例如,如果一個函數僅回傳 :ok,原子 :ok 本身永遠不會被覆蓋率考量進去;

  • 巨集,例如由 defmacro/2defguard/2 定義的巨集,以及僅由巨集呼叫的程式碼,除非它們也在測試期間被呼叫,否則永遠不會被視為已覆蓋。這是因為巨集是在編譯時呼叫的,在測試覆蓋率工具開始之前;

組態

:test_coverage 組態覆蓋率工具,並接受以下選項

  • :output - 覆蓋率結果的輸出目錄。預設為 "cover"

  • :tool - 指定要使用的覆蓋率工具的模組。

  • :summary - 在每次覆蓋率執行結束時,會印出每個模組的摘要,結果會以紅色或綠色顯示,具體取決於百分比是否低於或高於給定的閾值。如果總覆蓋率低於閾值,則任務將以狀態 1 退出。 :summary 選項允許您自訂摘要產生,預設為 [threshold: 90],但可以設定為 false 來停用此類報告。

  • :export - 輸出結果的檔案名稱,而不是動態產生覆蓋率結果。 .coverdata 副檔名會自動新增到給定的檔案。此選項會透過 --export-coverage 選項或使用程序分割自動設定。請參閱 mix test.coverage 以從多個輸出編譯報告。

  • :ignore_modules - 從報告和摘要中忽略的模組。它是作為原子和正規表示式的模組名稱清單,並與模組名稱相符。

  • :local_only - 預設情況下,覆蓋率只追蹤本機呼叫,如果您計畫跨節點執行覆蓋率,請將此選項設定為 false。

預設情況下,OTP 的 cover 包裝器會用作預設覆蓋率工具。您可以在 mix test.coverage 的文件了解它的運作方式。您的選擇工具可以如下提供

def project() do
  [
    ...
    test_coverage: [tool: CoverModule]
    ...
  ]
end

CoverModule 可以是任何匯出 start/2 的模組,接收編譯路徑和 test_coverage 選項作為引數。它必須回傳 nil 或一個零元函數,該函數將在測試套件完成後執行。

作業系統程序分割

雖然 ExUnit 支援在同一個 Elixir 執行個體中並行執行測試,但並非總是能夠並行執行所有測試。例如,有些測試可能依賴於全域資源。

基於這個原因,mix test 支援將測試檔案分割到不同的 Elixir 執行個體中。這透過設定 --partitions 選項為一個整數,其中包含分割數,以及設定 MIX_TEST_PARTITION 環境變數來控制特定執行個體正在執行的測試分割。如果您想將測試分佈到多部機器上,這也可能很有用。

例如,要將測試套件分割成 4 個分割,並執行它們,您會使用下列指令

$ MIX_TEST_PARTITION=1 mix test --partitions 4
$ MIX_TEST_PARTITION=2 mix test --partitions 4
$ MIX_TEST_PARTITION=3 mix test --partitions 4
$ MIX_TEST_PARTITION=4 mix test --partitions 4

測試檔案會以循環的方式事先排序。請注意,分割本身會提供為環境變數,因此可以在設定檔和測試腳本中存取它。例如,它可以在 config/test.exs 中為每個分割設定不同的資料庫執行個體。

如果已啟用分割,且使用了 --cover,則不會產生覆蓋率報告,因為它們只包含覆蓋率資料的子集。相反地,覆蓋率資料會匯出到檔案中,例如 cover/MIX_TEST_PARTITION.coverdata。一旦您在 cover/ 中擁有所有分割的結果,您可以執行 mix test.coverage 來取得統一的報告。

--stale 選項

--stale 命令列選項嘗試只執行自您上次使用 --stale 執行此任務以來,參考已變更模組的測試檔案。

第一次使用 --stale 執行此任務時,會執行所有測試並產生一個明細。在後續執行中,如果自上次使用 --stale 執行以來,任何它參考的模組(以及那些模組參考的任何模組,遞迴)已修改,則測試檔案會標記為「過時」。如果自上次使用 --stale 執行以來,測試檔案已變更,則它也會標記為「過時」。

--stale 選項對於軟體迭代極為有用,讓您在對程式碼庫進行變更時,僅執行相關測試。

檔案系統監視器

您可以透過命令列,使用 --listen-on-stdin 選項,將 mix test 與檔案系統監視器整合。例如,您可以使用 fswatch 或類似工具,在每次變更時發出換行符號,這將導致您的測試套件重新執行

$ fswatch lib test | mix test --listen-on-stdin

這可以與 --stale 選項結合使用,以重新執行已變更的測試檔案,以及由於 lib 中的變更而變為過時的測試。

中斷套件

可以使用 Ctrl+\ 中斷測試套件,這會將 SIGQUIT 訊號傳送給 Erlang VM。ExUnit 會攔截這個訊號,以顯示所有已中斷的測試,並列印到目前為止收集的結果。

這在套件卡住,而您不想等到逾時時間經過(預設為 30 秒)時,會很有用。