檢視原始碼 ExUnit.Case (ExUnit v1.16.2)

定義測試案例的輔助函式。

此模組必須用於其他模組,作為設定和準備測試的方法。

使用時,它接受下列選項

  • :async - 設定此模組中的測試與其他模組中的測試並行執行。同一個模組中的測試絕不會並行執行。僅在測試不會變更任何全域狀態時才應啟用。預設為 false

  • :register - 當為 false 時,不會在 ExUnit 伺服器中註冊此模組。這表示當 ExUnit 套件執行時,此模組不會執行。

使用 ExUnit.Case

當您 使用 ExUnit.Case 時,它會匯入 ExUnit.AssertionsExUnit.CallbacksExUnit.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 測試使用。

使用指定的環境註冊測試。

註冊一個函數,作為此案例的一部分執行。

定義一個未實作的測試,並附上字串。

定義一個測試,並附上 訊息

類型

函數

連結到此巨集

describe(訊息, 清單)

檢視原始碼 (自 1.3.0 起) (巨集)

描述測試。

每個 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 區塊,並確切知道所涉及的設定步驟。

連結到此函數

get_last_registered_test(mod)

檢視原始碼 (自 1.15.0 起)
@spec get_last_registered_test(env()) :: ExUnit.Test.t() | nil

傳回最近註冊的測試案例,作為 %ExUnit.Test{} 結構。

這會被第三方工具程式使用,以允許在編譯時使用測試標籤進行組態,而無需在執行時明確傳遞測試內容。它旨在於測試模組編譯之前在巨集中呼叫。

如果使用已編譯的模組呼叫,會引發錯誤。

連結到此函數

register_attribute(env, 名稱, 選項 \\ [])

檢視原始碼 (自 1.3.0 起)
@spec register_attribute(env(), atom(), keyword()) :: :ok

註冊一個新的屬性,供 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
連結到此函數

register_describe_attribute(env, 名稱, 選項 \\ [])

檢視原始碼 (自 1.10.0 起)
@spec register_describe_attribute(env(), atom(), keyword()) :: :ok

註冊一個新的描述屬性,供 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
連結到此函數

register_module_attribute(env, 名稱, 選項 \\ [])

檢視原始碼 (自 1.10.0 起)
@spec register_module_attribute(env(), atom(), keyword()) :: :ok

註冊一個新的模組屬性,供 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(map, test_type, name, tags)

檢視原始碼 (自 1.3.0 起)
此函數已棄用。請改用 register_test/6。

使用指定的環境註冊測試。

此函數已棄用,建議改用 register_test/6,後者在緊密迴圈中透過避免使用 __ENV__ 來提升效能。

連結到此函數

register_test(mod, 檔案, 行, 測試類型, 名稱, 標籤)

檢視原始碼 (自 1.10.0 起)

註冊一個函數,作為此案例的一部分執行。

這是由第三方專案(例如 QuickCheck)用來實作巨集,例如 property/3,其運作方式類似 test,但定義的是屬性。請參閱 test/3 實作,以取得呼叫此函式的範例。

測試類型會轉換為字串,並針對顯示進行複數化。您可以使用 ExUnit.plural_rule/2 設定自訂複數化。

定義一個未實作的測試,並附上字串。

提供一個方便的巨集,允許使用字串定義測試,但尚未實作。產生的測試將永遠失敗,並印出「未實作」錯誤訊息。產生的測試案例也會標記為 :not_implemented

範例

test "this will be a test in future"
連結到此巨集

test(訊息,變數 \\ 引用做 _ 結束,內容)

檢視原始碼 (巨集)

定義一個測試,並附上 訊息

測試也可以定義樣式,該樣式會與測試內容進行比對。如需有關內容的詳細資訊,請參閱 ExUnit.Callbacks

範例

test "true is equal to true" do
  assert true == true
end