【问题标题】:Is there a way to simulate the lack of internet connection in an Elixir test?有没有办法在 Elixir 测试中模拟缺乏互联网连接?
【发布时间】:2020-08-31 15:37:07
【问题描述】:

我正在处理用 Elixir 开发的命令行界面应用程序的覆盖率测试。该应用程序是 tldr-pages 的客户端,其功能包含在使用escript 构建的脚本中。为了执行这些操作,我在 HTTPoison.get/1 函数上使用了 case 结构,我在其中引入了格式化的 url。在这个case 中,我将响应与不同类型的值进行比较,例如页面是否存在,它会显示信息;如果不是,它将报告给用户,然后在另一个case 中继续评估其他可能性。最后,第一个case 完成了两个匹配错误的模式,一个用于缺少互联网连接,另一个用于意外错误。所描述的结构是下一个:

    case HTTPoison.get(process_url(os, term)) do
      {:ok, %HTTPoison.Response{status_code: 200, body: body}} ->
        IO.puts(body)

      {:ok, %HTTPoison.Response{status_code: 404}} ->
        IO.puts(
          "Term \"#{term}\" not found on \"#{os}\" pages\nExTldr is looking on \"common\" pages."
        )

        case HTTPoison.get(process_url("common", term)) do
          {:ok, %HTTPoison.Response{status_code: 200, body: body}} ->
            IO.puts(body)

          {:ok, %HTTPoison.Response{status_code: 404}} ->
            IO.puts("Term not found on \"common\" pages.")
        end

      {:error, %HTTPoison.Error{reason: reason}} when reason == :nxdomain ->
        raise NoInternetConnectionError

      {:error, %HTTPoison.Error{reason: reason}} when reason != :nxdomain ->
        raise UnexpectedError, reason
    end

NoInternetConnectionErrorUnexpectedError 是在另一个文件中定义的异常。最后的两种模式显然都很好,至少是第一种:

      {:error, %HTTPoison.Error{reason: reason}} when reason == :nxdomain ->
        raise NoInternetConnectionError

但是,正如我在问题开头所说的那样,我正在处理使用 GitHub Actions 和 Coveralls 自动执行的覆盖测试,并在依赖项中使用 ExCoveralls。在这个测试中,我收到了两个raise/1 语句的警告。尽管我理解这意味着什么可能是错误的,但我理解工作服报告了这种“遗漏的线条”或未覆盖的线条,因为我没有进行测试来涵盖这种情况。因此,我开始研究如何编写一个涵盖这两种情况的测试。

最重要的问题是如何在测试中模拟互联网连接的缺失来解决这个问题。我虽然即将开发一个模拟,但我在 Elixir 的一些模拟包中找不到对这种情况有用的东西,比如Mox。然后我找到了bypass,这是一个非常有趣的包,我认为它可能很有用,因为它有down/1up/1 来关闭和启动TCP 套接字,因此可以测试HTTP 服务器关闭时发生的情况。但是有了这个我有两个问题:

  1. 服务器宕机与用户部分缺乏互联网连接不同。
  2. 我尝试应用这种“上下”机制,但没有成功。我不打算分享它,因为我认为它不会是所描述的第一个问题的最终解决方案。

我不是用解决这个问题的代码来假装一个答案,我只是想了解这个测试应该如何工作以及我应该遵循的逻辑来开发它。我什至在研究 Erlang 文档,因为 Erlang 可能会提供本地函数来解决它(例如,我现在正在阅读 Erlang 的 Common Test Reference Manual,因为那里可能有一些有用的东西)。

编辑。我用bypass 尝试过的评论是安装依赖项,用bypass.open/0 编写一个设置,然后编写一个类似下一个的测试,其中我尝试用capture_io/1 断言捕获输出:

  test "lack of internet connection", %{bypass: bypass} do
    Bypass.down(bypass)

    execute_main = fn ->
      ExTldr.main([])
    end

    assert capture_io(execute_main) =~ "There is not internet connection"
  end

但是,正如我所想,它不包括可能的缺乏互联网的情况,只是检查服务器何时关闭的可能性。

【问题讨论】:

    标签: testing erlang elixir code-coverage coveralls


    【解决方案1】:

    旁注:我个人一直反对成为应该有助于开发的工具的奴隶。覆盖率是一个不错的指标,但不应将这些建议视为必须的。无论如何。

    我不确定你为什么将Mox 排除在外。经验法则是:测试不应涉及跨境呼叫,除非绝对不可避免。尽管如此,通过互联网进行的测试仍然不稳定:覆盖不会告诉你,我会。如果测试环境根本没有永久的互联网访问权限怎么办?临时连接问题?遥控器坏了?

    这正是Mox 诞生的原因。而且,幸运的是,HTTPoison 完全可以将Mox 用作模拟库,因为它声明了主操作模块HTTPoison.Base 的行为。

    您所需要的只是使您的实际 HTTP 客户端注入依赖项。大致上是这样的:

    
    @http_client Application.get_env(:my_app, :http_client, HTTPoison)
    
    ...
    
    case @http_client.get(process_url(os, term)) do
       ...
    end
    

    config/test.exs 中,您可以指定自己的:http_client,然后瞧——漂亮的模拟测试环境就是您的全部了。


    或者,您可以直接声明模拟:

    Mox.defmock(MyApp.HC, for: HTTPoison.Base)
    

    我还擅长调用第 3 方的应用程序级别的边界。也就是说,您可以为需要的外部 HTTP 调用定义自己的行为,并使用自己的包装器来实现此行为。这样一来,模拟会更容易,并且您将获得轻松更改真实客户端的好处。 HTTPoison 远不是当今最好的客户端(它几乎不支持 HTTP2 等),明天你可能会决定切换到 Mint。如果所有代码都位于包装器中,则完成起来会容易得多。

    【讨论】:

    • 谢谢你,Aleksei,非常有趣的答案(包括旁注)。在与 Mox 以及如何声明模拟(测试没有检测到它所以我最终在 test_helper.exs 而不是 test/support/mock.ex 中声明它)进行了很多斗争之后。但是现在我认为你的结论是最重要的部分:我应该定义一个行为来得到我所需要的,因为我不能指望 HTTPoison 返回我自己定义的异常。我在运行测试时意识到了这一点(gist with my current situation)。我会努力的!
    • Aleksei,在我在上一条评论中分享的要点中,我使用了两个配置文件 config/config.exsconfig/test.exs,但我知道如果我使用 @http_client Application.get_env(:my_app, :http_client, HTTPoison) 注入依赖项然后创建在test_helper.exs 中模拟,配置文件不是必需的,不是吗?至少在没有它们的情况下它似乎可以工作,因为我备份了配置文件然后将其删除,它仍然可以工作。对不起,虽然现在我对 Mox 的模拟内容有了更清楚的了解,但在阅读了许多不同的内容后,我对配置文件感到有些困惑。
    • 当然。我刚刚说出了所有可能的方法:) 请参阅两者之间的水平规则。 Mox 是最好的方法,因此您可以轻松摆脱依赖注入。 OTOH,如果您想在运行时支持不同的客户端_,依赖注入可以用于测试不需要模拟。
    猜你喜欢
    • 2015-04-08
    • 2016-01-09
    • 2011-06-21
    • 1970-01-01
    • 2018-12-31
    • 1970-01-01
    • 2011-08-25
    • 2015-12-24
    • 1970-01-01
    相关资源
    最近更新 更多