我正在尝试在我的Elixir项目中使用名为Mox的模拟库,但即使按照官方文档: https://hexdocs.pm/mox/Mox.html
我无法为模块功能定义新行为。尝试运行测试时,它给出了以下错误:
**(ArgumentError)模块Myapp.MyModule不是行为,请将行为传递给:for (mox)lib / mox.ex:210:Mox.validate_behaviour!/ 1 (mox)lib / mox.ex:198:Mox.defmock / 2 (elixir)lib / code.ex:376:Code.require_file / 2 (elixir)lib / enum.ex:678:Enum。“ - 每个/ 2个列表^ foreach / 1-0 - ”/ 2 (elixir)lib / enum.ex:678:Enum.each / 2
这是我到目前为止所尝试的内容:
test_helper.exs:
ExUnit.start()
Mox.defmock(Myapp.MockMyModule, for: Myapp.MyModule)
MyModuleTest.exs
defmodule MyModuleTest do
use ExUnit.Case
import Mox
setup :verify_on_exit!
test "Test status processor in transit with mocked result" do
Myapp.MyModule
|> expect (:put_calculated_eta, fn body, shipment_id, authorization_key -> {:ok, "bla", 200} end)
map = #Some data that fits the function interface
assert {:ok, "bla", 200} == Myapp.MyModule.update_shipment_eta(map)
end
end
我认为最奇怪的是,在文档中它确实希望模块被传递用于模拟,但是在错误中它要求行为(我想应该是函数,我已经尝试过但没有成功...谁能告诉我为什么我的模块不能被嘲笑?
只是为了澄清,这种嘲弄是针对外部api的回应。
如果我没有提供足够的信息,请告诉我。
答案 0 :(得分:4)
错误消息传达的缺失是行为。 Elixir Behaviors(注意英国拼写)类似于强类型OOP语言中的“接口”;也就是说,它们描述了其他模块实现的一组功能。
例如,此模块使用单个回调方法do_work/1
定义一个行为,该方法需要一个整数形式的作业,并返回{:ok, result}
或{:error, reason}
:
defmodule WorkBehavior do
@callback do_work(job::integer) :: {:ok, integer} | {:error, atom}
end
这些模块实现了这种行为:
defmodule LazyWorker do
@behaviour WorkBehavior
@impl WorkBehavior
def do_work(job), do: {:error, :too_tired}
end
defmodule HardWorker do
@behaviour WorkBehavior
@impl WorkBehavior
def do_work(job), do: {:ok, job + 42}
end
行为的最终目标是允许您编写模块化代码,可以轻松地交换各种实现。使用上面的代码,worker模块可以作为参数传递,而不是将其硬编码。比较:
def closely_coupled do
HardWorker.do_work(12)
end
VS
def loosely_coupled(worker) do
worker.do_work(12)
end
Mox要求您做的是定义一个捕获MyApp.MyModule
API的行为,以便Mox可以创建一个虚假版本供您测试。 JoséValim在this post中解释了这种方法的动机,而不是更典型的动态模拟。
如果没有看到MyApp.MyModule的代码,我无法准确地告诉您要编写的代码,但一般情况下,您应该为每个想要模拟的方法添加@callback
个标记。有关typespecs和行为的介绍,请参阅this page(提示:@callback
标记使用typespec语法,因此请阅读整个页面。
答案 1 :(得分:0)
您需要定义Myapp.MyModule
模块的行为。
如果您不想编写单独的模块来描述https://stackoverflow.com/a/48475734/438990中建议的行为,则只需添加 公共功能的@callback描述符。
defmodule Myapp.MyModule
@callback put_calculated_eta(String.t()) :: String.t()
def put_calculated_eta(fiz)
fiz + "buz"
end
end
然后您可以使用
注册该模拟游戏Mox.defmock(Myapp.MockMyModule, for: Myapp.MyModule)
代替
Mox.defmock(Myapp.MockMyModule, for: Myapp.MyModuleBehavior)