mocking - 使用 Mox 模拟哈克尼
问题描述
我正在学习 Elixir 和 Phoenix 堆栈,然后我进入了一个使用 Hackney 模块 (erlang) 进行 http 调用的生产项目。这里的情况是我想模拟 hackney 的“请求”函数,所以当它被调用时,它会返回一个特定的响应。到目前为止,我已经四处奔波并登陆 Mox 库来实现这一点,但是由于 hackney 是一个 Erlang 模块,而 Mox 抱怨它不是一种行为,我想知道是否有更好的方法来做到这一点或者也许还有另一个缺少的配置。代码失败的地方是在模拟定义的开头:
Mox.defmock(MyApp.MockHackney, for: :hackney)
这只是失败了module :hackney is not a behaviour, please pass a behaviour to :for
我假设我做错了什么(我在这里关注 Mox 库的文档:https ://hexdocs.pm/mox/Mox.html 了解行为是什么,我了解错误,事情是我不知道是否有办法为这个模拟“注入”行为定义给哈克尼,或者我使用了错误的工具来完成这项工作。任何想法都将不胜感激,在此先感谢 :)
解决方案
我将与您分享一种我用来模拟各种 3rd 方库的方法Mox
,当它们没有定义自己的行为时。
首先:定义您自己的 Elixir 行为,该行为定义与您从该库中使用的函数相对应的回调。例如,如果您正在调用,:hackney.get/2
则在您的行为中为该函数定义一个回调。定义一个您可能实际上并未在代码中使用的行为可能看起来毫无意义,但它为您做了两件重要的事情:
- 它可以帮助您记录您正在使用的给定模块中的哪些功能,因此它是对该功能进行抽象的第一步(想想如果您突然不得不更换使用的 HTTP 客户端库,您可能需要如何重构事物)。
- 它满足了 Mox 对具有可以通过反射检查的行为的需求。
例如
defmodule MyClientBehaviour do
@callback get(url :: binary()) :: {:ok, any()} | {:error, any()}
end
test/
请注意,如果它确实仅用于测试,您可以在目录中定义行为。
第二:调整您想要测试的代码,以便您可以在运行时提供模块。有两种常见的方法可以做到这一点:
- 通过提供的
opt
, 或 - 从配置中解析模块。
例如,如果您的代码:hackney
用于拨打电话,例如
def get(url, opts \\ []) do
:hackney.get(url)
end
您可以改为提供覆盖选项,例如
def get(url, opts \\ []) do
client = Keyword.get(opts, :client, :hackney)
client.get(url)
end
这样,您可以保留默认实现(:hackney
在您的情况下),您现在可以在测试期间传入模拟。通常,您test_helper.exs
将声明正在使用的模拟,例如
# test_helper.exs
ExUnit.start()
Mox.Server.start_link([])
Mox.defmock(HTTPClientMock, for: MyClientBehaviour)
defmodule YourTest do
use ExUnit.Case
import Mox
setup :verify_on_exit!
test ":ok something" do
client =
HTTPClientMock
|> expect(:get, fn _ ->
{:ok, "some response here"}
end)
assert {:ok, _} = YourModule.get("http://example.com", client: client)
end
end
如果您调用:hackey
的位置嵌入得太深而无法进行这种opts
注入,则将其定义为可配置模块会很有用,例如
def get(url, opts \\ []) do
client = Application.get_env(:my_app, :http_client, :hackney)
client.get(url)
end
在这种情况下,您可以在运行测试之前将值放入应用程序配置中(或者您可以将其添加到test.exs
配置中)。这最好在一个setup
块中完成,以确保它在测试之前完成,并确保在完成后将其设置回来。
setup do
http_client = Application.get_env(:my_app, :http_client)
on_exit(fn ->
Application.put_env(:my_app, :http_client, http_client)
end)
end
推荐阅读
- bash - 停止在 bash 脚本中生成的 tcpdump
- typescript - 'string | 类型的参数 号码 | number[]' 不能分配给'number[]' 类型的参数?
- sql-server - sql - 基于两列但不同行的相同值将同一表中的多行合并到一个表中
- go - 如何通过fabric-sdk-go中的TxnID获取存储在账本中的数据?
- c++ - 为什么'class'和'main'中的相等表达式表现不同?
- maven - dependency-check-maven 插件不检查 package.json 依赖项
- nginx - NGINX 重写规则对性能有多大影响
- powershell - 如何使用此代码发送带有多个附件的电子邮件
- mysql - 在列中保存 DATEDIFF 的 MySql 问题
- angular - Angular 8:FormArray.clear() 没有清除我的 FormArray