Python允许您甚至模拟os.listdir
以进行测试。你能用Elixir的模拟库做同样的事情吗?一个例子很好。
答案 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
这个库略有不同,它可以帮助您为现有模块创建存根,然后可以将其作为参数传递给其他函数:
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
此库使用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
与注射器类似,必须修改代码以使用动态代理进行测试。使用此库时,您使用模块属性声明依赖项:
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相比的优势:
另请参见my Elixir forum post about MecksUnit。请注意,您也可以断言函数调用。简单易行是MecksUnits的主要目标。希望它可以帮助您。如果是这样,祝你好运!