檢視原始碼 ExUnit.Case (ExUnit v1.16.2)
定義測試案例的輔助函式。
此模組必須用於其他模組,作為設定和準備測試的方法。
使用時,它接受下列選項
:async
- 設定此模組中的測試與其他模組中的測試並行執行。同一個模組中的測試絕不會並行執行。僅在測試不會變更任何全域狀態時才應啟用。預設為false
。:register
- 當為false
時,不會在 ExUnit 伺服器中註冊此模組。這表示當 ExUnit 套件執行時,此模組不會執行。
使用 ExUnit.Case
當您
使用 ExUnit.Case
時,它會匯入ExUnit.Assertions
、ExUnit.Callbacks
、ExUnit.DocTest
和此模組本身的功能。
範例
defmodule AssertionTest do
# Use the module
use ExUnit.Case, async: true
# The "test" macro is imported by ExUnit.Case
test "always pass" do
assert true
end
end
內容
所有測試都會收到一個內容作為引數。內容特別適用於在回呼和測試之間分享資訊
defmodule KVTest do
use ExUnit.Case
setup do
{:ok, pid} = KV.start_link()
{:ok, pid: pid}
end
test "stores key-value pairs", context do
assert KV.put(context[:pid], :hello, :world) == :ok
assert KV.get(context[:pid], :hello) == :world
end
end
由於內容是一個映射,因此可以比對樣式以擷取資訊
test "stores key-value pairs", %{pid: pid} = _context do
assert KV.put(pid, :hello, :world) == :ok
assert KV.get(pid, :hello) == :world
end
標籤
內容用於將資訊從回呼傳遞到測試。為了將資訊從測試傳遞到回呼,ExUnit 提供標籤。
透過標記測試,標記值可以在背景中存取,讓開發人員自訂測試。讓我們看一個範例
defmodule FileTest do
# Changing directory cannot be async
use ExUnit.Case, async: false
setup context do
# Read the :cd tag value
if cd = context[:cd] do
prev_cd = File.cwd!()
File.cd!(cd)
on_exit(fn -> File.cd!(prev_cd) end)
end
:ok
end
@tag cd: "fixtures"
test "reads UTF-8 fixtures" do
File.read("README.md")
end
end
在上面的範例中,我們定義了一個名為 :cd
的標記,在設定回呼中讀取它,以設定測試將執行的作業目錄。
標記在與案例範本 (ExUnit.CaseTemplate
) 搭配使用時也非常有效,允許案例範本中的回呼自訂測試行為。
請注意,標記可以用兩種不同的方式設定
@tag key: value
@tag :key # equivalent to setting @tag key: true
如果標記給予多次,最後一個值會獲勝。
模組和描述標記
可以透過在每個背景中分別設定 @moduletag
或 @describetag
,為模組或描述區塊中的所有測試設定標記
defmodule ApiTest do
use ExUnit.Case
@moduletag :external
describe "makes calls to the right endpoint" do
@describetag :endpoint
# ...
end
end
如果您要設定 @moduletag
或 @describetag
屬性,您必須在呼叫 use ExUnit.Case
之後設定它們,否則您會看到編譯錯誤。
如果透過 @tag
設定相同的鍵,則 @tag
值具有較高的優先權。
setup_all
區塊僅接收使用 @moduletag
設定的標記。
已知標記
以下標記由 ExUnit 自動設定,因此是保留的
:module
- 定義測試的模組:file
- 定義測試的檔案:line
- 定義測試的列:test
- 測試名稱:async
- 如果測試案例處於非同步模式:registered
- 用於ExUnit.Case.register_attribute/3
值:describe
- 測試所屬的描述區塊:describe_line
- 描述區塊開始的列:doctest
- 模組或檔案正在進行 doctest(如果是 doctest):doctest_line
- 定義 doctest 的行(如果是 doctest):doctest_data
- 有關 doctest 的其他元資料(如果是 doctest):test_type
- 列印測試結果時使用的測試類型。ExUnit 會將它設定為:test
、:doctest
等,但可以自訂。
下列標籤會自訂測試的行為
:capture_log
- 參閱下方的「記錄擷取」區段:skip
- 以給定的原因略過測試:timeout
- 自訂測試逾時時間(預設為 60000),以毫秒為單位。接受:infinity
作為逾時值。:tmp_dir
-(自 v1.11.0 起)參閱下方的「暫存目錄」區段
篩選器
標籤也可以用於識別特定測試,然後可以使用篩選器包含或排除這些測試。最常見的功能是排除某些特定測試不執行,這可以使用 ExUnit.configure/1
來完成
# Exclude all external tests from running
ExUnit.configure(exclude: [external: true])
從現在開始,ExUnit 不會執行任何已將 :external
選項設定為 true
的測試。此行為可以使用通常透過命令列傳遞的 :include
選項還原
$ mix test --include external:true
執行 mix help test
以取得更多有關如何透過 Mix 執行篩選器的資訊。
標籤和篩選器的另一種使用案例是預設排除所有具有特定標籤的測試(不論其值為何),並只包含特定子集
ExUnit.configure(exclude: :os, include: [os: :unix])
可以多次提供特定包含/排除篩選器
ExUnit.configure(exclude: [os: :unix, os: :windows])
請記住,預設會包含所有測試,因此除非先將它們排除,否則 include
選項不會有任何作用。
記錄擷取
ExUnit 可以選擇性地抑制在測試期間產生的記錄訊息的列印。在執行測試時產生的記錄訊息會被擷取,而且只有在測試失敗時才會列印這些訊息以協助除錯。
您可以透過標記 :capture_log
來針對個別測試選擇此行為,或在 ExUnit 組態中啟用所有測試的日誌擷取
ExUnit.start(capture_log: true)
此預設值可以透過 @tag capture_log: false
或 @moduletag capture_log: false
覆寫。
由於 setup_all
區塊不屬於特定測試,因此在其中 (或測試之間) 產生的日誌訊息絕不會被擷取。如果您也想要抑制這些訊息,請透過設定來移除主控台後端
config :logger, backends: []
暫存目錄
ExUnit 會自動為標記為 :tmp_dir
的測試建立暫存目錄,並將該目錄的路徑放入測試內容。目錄會在建立前移除,以確保我們從空白開始。
暫存目錄路徑是唯一的 (包含測試模組和測試名稱),因此適合同時執行測試。您可以透過將標記設定為字串來進一步自訂路徑,例如:tmp_dir: "my_path"
,這會讓最終路徑變成:tmp/<module>/<test>/my_path
。
範例
defmodule MyTest do
use ExUnit.Case, async: true
@tag :tmp_dir
test "with tmp_dir", %{tmp_dir: tmp_dir} do
assert tmp_dir =~ "with tmp_dir"
assert File.dir?(tmp_dir)
end
end
與其他標記一樣,:tmp_dir
也可以設定為 @moduletag
和 @describetag
。
摘要
函式
描述測試。
傳回最近註冊的測試案例,作為 %ExUnit.Test{}
結構。
註冊一個新的屬性,供 ExUnit.Case
測試使用。
註冊一個新的描述屬性,供 ExUnit.Case
測試使用。
註冊一個新的模組屬性,供 ExUnit.Case
測試使用。
使用指定的環境註冊測試。
註冊一個函數,作為此案例的一部分執行。
定義一個未實作的測試,並附上字串。
定義一個測試,並附上 訊息
。
類型
@type env() :: module() | Macro.Env.t()
函數
描述測試。
每個 describe 區塊會收到一個名稱,用作後續測試的前置詞。在一個區塊內,ExUnit.Callbacks.setup/1
可能會被呼叫,而且它會定義一個設定回呼,只會為目前的區塊執行。describe 名稱也會被加入為標籤,讓開發人員可以執行特定區塊的測試。
範例
defmodule StringTest do
use ExUnit.Case, async: true
describe "String.downcase/1" do
test "with ascii characters" do
assert String.downcase("HELLO") == "hello"
end
test "with Unicode" do
assert String.downcase("HÉLLÒ") == "héllò"
end
end
end
在使用 Mix 時,你可以透過名稱執行 describe 區塊中的所有測試
$ mix test --only describe:"String.downcase/1"
或透過傳遞 describe 區塊開始的確切行數執行
$ mix test path/to/file:123
請注意 describe 區塊無法巢狀。開發人員不應依賴層級來進行組成,而應建立在已命名設定之上。例如
defmodule UserManagementTest do
use ExUnit.Case, async: true
describe "when user is logged in and is an admin" do
setup [:log_user_in, :set_type_to_admin]
test ...
end
describe "when user is logged in and is a manager" do
setup [:log_user_in, :set_type_to_manager]
test ...
end
defp log_user_in(context) do
# ...
end
end
透過禁止層級並改用已命名設定,開發人員可以輕鬆瀏覽每個 describe 區塊,並確切知道所涉及的設定步驟。
@spec get_last_registered_test(env()) :: ExUnit.Test.t() | nil
傳回最近註冊的測試案例,作為 %ExUnit.Test{}
結構。
這會被第三方工具程式使用,以允許在編譯時使用測試標籤進行組態,而無需在執行時明確傳遞測試內容。它旨在於測試模組編譯之前在巨集中呼叫。
如果使用已編譯的模組呼叫,會引發錯誤。
註冊一個新的屬性,供 ExUnit.Case
測試使用。
屬性值可透過 context.registered
取得。已註冊的值會在每次 test/3
後清除,類似於 @tag
。
此函數採用與 Module.register_attribute/3
相同的選項。
範例
defmodule MyTest do
use ExUnit.Case
ExUnit.Case.register_attribute(__MODULE__, :fixtures, accumulate: true)
@fixtures :user
@fixtures {:post, insert: false}
test "using custom attribute", context do
assert context.registered.fixtures == [{:post, insert: false}, :user]
end
test "custom attributes are cleared per test", context do
assert context.registered.fixtures == []
end
end
註冊一個新的描述屬性,供 ExUnit.Case
測試使用。
屬性值可透過 context.registered
取得。已註冊的值會在每次 describe/2
後清除,類似於 @describetag
。
此函數採用與 Module.register_attribute/3
相同的選項。
範例
defmodule MyTest do
use ExUnit.Case
ExUnit.Case.register_describe_attribute(__MODULE__, :describe_fixtures, accumulate: true)
describe "using custom attribute" do
@describe_fixtures :user
@describe_fixtures {:post, insert: false}
test "has attribute", context do
assert context.registered.describe_fixtures == [{:post, insert: false}, :user]
end
end
describe "custom attributes are cleared per describe" do
test "doesn't have attributes", context do
assert context.registered.describe_fixtures == []
end
end
end
註冊一個新的模組屬性,供 ExUnit.Case
測試使用。
屬性值可透過 context.registered
取得。
此函數採用與 Module.register_attribute/3
相同的選項。
範例
defmodule MyTest do
use ExUnit.Case
ExUnit.Case.register_module_attribute(__MODULE__, :module_fixtures, accumulate: true)
@module_fixtures :user
@module_fixtures {:post, insert: false}
test "using custom attribute", context do
assert context.registered.module_fixtures == [{:post, insert: false}, :user]
end
test "still using custom attribute", context do
assert context.registered.module_fixtures == [{:post, insert: false}, :user]
end
end
使用指定的環境註冊測試。
此函數已棄用,建議改用 register_test/6
,後者在緊密迴圈中透過避免使用 __ENV__
來提升效能。
註冊一個函數,作為此案例的一部分執行。
這是由第三方專案(例如 QuickCheck)用來實作巨集,例如 property/3
,其運作方式類似 test
,但定義的是屬性。請參閱 test/3
實作,以取得呼叫此函式的範例。
測試類型會轉換為字串,並針對顯示進行複數化。您可以使用 ExUnit.plural_rule/2
設定自訂複數化。
定義一個未實作的測試,並附上字串。
提供一個方便的巨集,允許使用字串定義測試,但尚未實作。產生的測試將永遠失敗,並印出「未實作」錯誤訊息。產生的測試案例也會標記為 :not_implemented
。
範例
test "this will be a test in future"
定義一個測試,並附上 訊息
。
測試也可以定義樣式,該樣式會與測試內容進行比對。如需有關內容的詳細資訊,請參閱 ExUnit.Callbacks
。
範例
test "true is equal to true" do
assert true == true
end