簡體   English   中英

如何在 Elixir 中模擬模塊屬性和其他模塊?

[英]How to mock module attributes and other modules in Elixir?

我對長生不老葯很新,所以這可能是基本的,但在網上看不到太多

如果我有以下代碼

defmodule A do
  def my_first_function do
    # does stuff
  end
end

defmodule B do
    @my_module_attribute A.my_first_function()
end

那么如何在我的測試中模擬模塊A以便我可以告訴測試我希望它返回什么?

也可以只模擬@my_module_attribute嗎? 對這兩種方法都有一個答案會很好(然后什么被認為是更好的模式)

我認為您可能希望將模塊屬性用於更多用途,並且可能對“模擬”的確切定義有些混淆。

避免將模塊屬性視為“類變量”——即使它們看起來可能用於相同的目的並且它們位於相同的位置,但它們的行為是不同的。 當模塊屬性依賴於函數調用時要特別小心。 一個常見的陷阱是使用模塊屬性來存儲從配置中讀取的值,例如使用Application.fetch_env!/2 問題是模塊屬性是在編譯時(而不是在運行時)評估的,所以你很容易得到一個意想不到的值。 (編譯器現在明確警告這個問題,現在提供了Application.compile_env!/2以更好地傳達該特定用例)。

我通常保留模塊屬性以提高簡單常量的可見性,並且我傾向於避免使用它們來存儲任何函數執行的結果。

當它回到“模擬”時,你仍然需要考慮 Elixir 是編譯的事實——它不是 Javascript。 比我更極客的人可以解釋這些機制,但是模塊屬性在運行時的存在方式與編譯時不同。

測試期間的“模擬”通常意味着將一個模塊或函數替換為另一個,並且這種交換通常在運行時更容易進行。 一種常見的模式看起來有點像依賴注入(但純粹主義者可能會反對這種比較)。

考慮一個像這樣的函數,它依賴於一些OtherModule來完成它的工作:

def my_function(input, opts \\ []) do
  other_module = Keyword.get(opts, :service, OtherModule)
  other_module.get_thing(input)
  # do more stuff...
end

不是在my_function中對OtherModule進行硬編碼,而是從可選的opts中讀出其名稱。 當您需要對其進行測試時,這提供了一種有用的方法來覆蓋該OtherModule的輸出。

在您的測試中,您可以執行以下操作:

test "example mock" do
      assert something = MyApp.MyMod.my_function("foo", service: MockService)
end

當測試提供MockService作為:service模塊時, get_thing函數將在MockService模塊上調用,而不是在OtherModule上。 如果提供的模塊沒有定義get_thing函數,代碼將無法執行。 這就是行為派上用場的地方,因為它有助於確保您的模塊已實現所需的功能。 例如, Mox測試庫依賴於行為契約,但從上面的示例中您可以看到前提。

如果你眯着眼睛,你會發現這種“注入”方法有點類似於 Javascript 通常接受回調函數作為參數的方式,但在 Elixir 中更常見的是傳遞模塊名稱而不是捕獲的函數。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM