You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Hammox测试Phoenix控制器?启动遇模块不存在错误

背景

我正尝试为自定义Behaviour创建Mock,使用的是Hammox(Mox的扩展版本)。已在config/test.exs中配置应用使用StorageMock:

config :my_app, storage: StorageMock

测试文件test/my_app/application_test.exs内容如下:

defmodule MyApp.ApplicationTest do
  @moduledoc false

  use ExUnit.Case, async: false
  use MyApp.ConnCase

  import Hammox

  test "client can handle an error response", %{conn: conn} do
    Hammox.defmock(StorageMock, for: MyApp.Storage)

    expect(StorageMock, :get, fn args ->
      assert args == "Chicago"

      # 这里定义Mock的返回值
      {:ok, %{body: "Some html with weather data"}}
    end)

    get(conn, "~p/api/users/validate")
  end
end

我参考了Hammox文档和Phoenix控制器测试指南,尝试测试控制器端点。

问题

当前配置存在问题,启动应用时出现如下错误:

Compiling 1 file (.ex)
** (Mix) Could not start application my_app: exited in: MyApp.Application.start(:normal, [])
    ** (EXIT) an exception was raised:
        ** (ArgumentError) The module StorageMock was given as a child to a supervisor but it does not exist
            (elixir 1.16.0) lib/supervisor.ex:797: Supervisor.init_child/1
            (elixir 1.16.0) lib/enum.ex:1700: Enum."-map/2-lists^map/1-1-"/2
            (elixir 1.16.0) lib/enum.ex:1700: Enum."-map/2-lists^map/1-1-"/2
            (elixir 1.16.0) lib/supervisor.ex:783: Supervisor.init/2
            (elixir 1.16.0) lib/supervisor.ex:707: Supervisor.start_link/2
            (kernel 9.2) application_master.erl:293: :application_master.start_it_old/4

我认为需要告知VMStorageMock是来自Hammox的模块,但找不到对应设置,请问我遗漏了什么配置?

解决方案

问题核心是应用启动时StorageMock还未被定义——你在单个测试用例内部调用Hammox.defmock/2,但应用启动流程(包括supervisor初始化子进程)早于测试代码执行,此时VM找不到这个Mock模块。

具体解决步骤:

  1. 提前定义Mock模块
    不要在测试用例里临时定义Mock,把Hammox.defmock/2放到测试目录的support文件中,比如新建test/support/mocks.ex:

    Hammox.defmock(StorageMock, for: MyApp.Storage)
    

    然后在test/test_helper.exs中引入这个文件,确保所有测试启动前Mock模块已加载:

    # test/test_helper.exs
    ExUnit.start()
    Code.require_file("test/support/mocks.ex")
    
  2. 检查应用启动的子进程配置
    如果你的MyApp.Application里把storage配置的模块作为supervisor子进程启动,要注意:Hammox的Mock是纯函数模块,不是GenServer这类可启动的进程。如果原MyApp.Storage是需要启动的进程,要调整启动逻辑:

    • 生产环境启动真实的Storage进程
    • 测试环境不需要启动Mock进程,或者改用Hammox的进程Mock方式(适配GenServer类型的模块)
  3. 清理测试用例中的冗余代码
    移除测试用例内部的Hammox.defmock/2调用,直接使用已提前定义好的StorageMock:

    defmodule MyApp.ApplicationTest do
      @moduledoc false
    
      use ExUnit.Case, async: false
      use MyApp.ConnCase
    
      import Hammox
    
      test "client can handle an error response", %{conn: conn} do
        expect(StorageMock, :get, fn args ->
          assert args == "Chicago"
          {:ok, %{body: "Some html with weather data"}}
        end)
    
        get(conn, "~p/api/users/validate")
      end
    end
    
  4. 可选:配置Hammox全局参数
    可以在config/test.exs中添加Hammox的全局配置,强化Mock的严格性:

    config :hammox,
      global: true,
      strict: true
    

内容的提问来源于stack exchange,提问作者Flame_Phoenix

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 13:46:24