是否有可能在elixir中模拟File.ls?

时间:2017-08-03 08:50:49

标签: mocking elixir

Python允许您甚至模拟os.listdir以进行测试。你能用Elixir的模拟库做同样的事情吗?一个例子很好。

2 个答案:

答案 0 :(得分:4)

Elixir有many mocking libraries,大多数都基于erlang的meck库。 Jose Valim的This blog post涵盖了模拟框架的替代方案,支持简单的存根,协议或回调函数。

Mock

如果您希望全局存根可以从您无法控制的代码调用的函数(第三方软件包或标准库),这是一个不错的选择。缺点是使用这种方法时,您的测试不能异步运行。

 use ExUnit.Case, async: false
 import Mock

 test "test_name" do
    with_mock HTTPotion, [get: fn(_url) -> "<html></html>" end] do
      HTTPotion.get("http://example.com")
      # Tests that make the expected call
      assert called HTTPotion.get("http://example.com")
    end
  end

Stubr

这个库略有不同,它可以帮助您为现有模块创建存根,然后可以将其作为参数传递给其他函数:

test "create a stub of Timex.now/0 and defer on all other functions" do
  fixed_time = Timex.to_datetime({2999, 12, 30})

  timex_stub = Stubr.stub!([now: fn -> fixed_time end], module: Timex, auto_stub: true)

  assert timex_stub.now == fixed_time
  assert timex_stub.before?(fixed_time, timex_stub.shift(fixed_time, days: 1))
end

Syringe

此库使用inject宏来允许您在运行测试时交换模块依赖项。它要求您修改代码,但作为回报,您可以异步运行测试。

defmodule MyThing do
  def do_mine_things do
    1 + 2
  end
end

defmodule MyModule do
  use Injector

  inject MyThing, as: Mine

  def do_things do
    Mine.do_mine_things
  end
end

defmodule MyModuleTest do
  use ExUnit.Case, async: true
  import Mocker

  test "Mine must be called" do
    mock(MyThing)
    assert MyModule.do_things == nil
    intercept(MyThing, :do_mine_things, nil, with: fn() -> "my mocked return" end)
    assert MyModule.do_things == "my mocked return"
    assert was_called(MyThing, :do_mine_things, nil) == twice # success
  end
end

Mockery

与注射器类似,必须修改代码以使用动态代理进行测试。使用此库时,您使用模块属性声明依赖项:

defmodule MyApp.Controller do
  @service Mockery.of(MyApp.UserService)

  def all do
    @service.users()
  end
end

然后在测试中:

# mock MyApp.UserService.users/0
mock MyApp.UserService, [users: 0], "mock"
assert MyApp.Controller.all() == "mock"

答案 1 :(得分:0)

由于我缺少在Elixir中轻松模拟任何功能的能力,因此我决定编写十六进制包MecksUnit。它使您可以轻松地模拟模块功能,而无需任何麻烦。

scala> def sortJs(js: JsValue): JsValue = js match {
     |     case JsObject(fields) => JsObject(fields.sortBy(_._1).map { case (k, v) => (k, sortJs(v)) })
     |     case _ => js
     | }
<console>:16: error: value sortBy is not a member of scala.collection.Map[String,play.api.libs.json.JsValue]
           case JsObject(fields) => JsObject(fields.sortBy(_._1).map { case (k, v) => (k, sortJs(v)) })
                                                    ^
<console>:16: error: type mismatch;
 found   : Any
 required: play.api.libs.json.JsValue
           case JsObject(fields) => JsObject(fields.sortBy(_._1).map { case (k, v) => (k, sortJs(v)) })
                                                                                                 ^

在代码示例中,我模拟了defmodule MecksUnit.FooTest do use ExUnit.Case, async: true use MecksUnit.Case defmock List do def wrap(:foo_test), do: ~w(MecksUnit Bar Test) end mocked_test "parallel compiling" do task = Task.async(fn -> assert [:foo, :bar] == List.wrap([:foo, :bar]) assert ~w(MecksUnit Bar Test) == List.wrap(:foo_test) assert called(List.wrap(:foo_test)) end) Task.await(task) end end 函数。使用MecksUnit与Mock相比的优势:

  1. 可以异步运行测试^^(注意:模拟的模块是按测试隔离的)
  2. 定义模拟函数更具可读性/自然性

另请参见my Elixir forum post about MecksUnit。请注意,您也可以断言函数调用。简单易行是MecksUnits的主要目标。希望它可以帮助您。如果是这样,祝你好运!

相关问题