[英]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.