模拟Mox Library错误:模块Myapp.MyModule不是行为,请将行为传递给:for

时间:2018-01-26 19:39:26

标签: mocking elixir

我正在尝试在我的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的回应。

如果我没有提供足够的信息,请告诉我。

2 个答案:

答案 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)