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

ExUnit测试顺序控制及去耦合改写技术咨询

ExUnit测试顺序控制与无依赖重构方案

针对你遇到的测试依赖顺序问题,以下是两个问题的具体解决方案:


1. 让测试用例按顺序执行

ExUnit默认随机执行测试是为了暴露依赖问题,但如果需要强制按定义顺序执行,有两种实用方式:

方式一:给整个模块添加顺序标签

在测试模块顶部添加@moduletag :sequential,该模块内的所有测试会严格按照函数定义的顺序执行:

defmodule GroupTest do
  use ExUnit.Case
  @moduletag :sequential  # 启用顺序执行
  alias Chat.CentralServer, as: Server
  alias Chat.Client, as: Client

  # 剩余代码保持不变
end

方式二:给单个测试指定执行顺序

如果只需控制部分测试的顺序,可给每个测试函数添加@tag order: 数字标签,数字越小优先级越高:

@tag order: 1
test "group creation test" do
  # 测试逻辑
end

@tag order: 2
test "group join test" do
  # 测试逻辑
end

@tag order: 3
test "send group message test" do
  # 测试逻辑
end

2. 改写代码消除依赖,实现测试独立

依赖执行顺序的测试维护成本高,正确的做法是让每个测试独立运行,自行准备所需前置环境。具体重构步骤如下:

步骤1:将全局初始化移至setup,每个测试前重置状态

把原来的setup_all替换为setup,确保每个测试开始前都清理环境并初始化基础数据,同时通过返回值传递常用变量,避免重复解构:

setup do
  # 清理群组相关文件
  File.rm_rf!(Chat.Config.get_group_file())
  File.rm_rf!(Chat.Config.get_group_msg_folder())
  File.rm_rf!(Chat.Config.get_group_users_folder())
  # 重启系统保证状态干净
  {:ok, _} = Chat.System.start_link()

  # 注册所有测试用户
  Enum.each(@clients, fn client ->
    {:ok, _pid} = Client.start_link(client)
    {:success} = Client.signup(client)
  end)

  # 传递变量给测试函数
  {:ok, clients: @clients, groups: @groups}
end

步骤2:每个测试自行准备前置条件

不再依赖其他测试的执行结果,每个测试内部创建所需的群组、成员关系等:

test "send group message test", %{clients: [lorem, _, _, _], groups: [synergy_squad, _, _]} do
  IO.puts("\n\nsend group message test\n\n")
  # 自行创建测试所需的群组
  {:success} = Client.create_group(lorem, synergy_squad)
  # 执行消息发送测试
  Client.send_group_message(lorem, synergy_squad, "@synergy_squad  meet in 30 mins.")
end

步骤3:提取重复逻辑为辅助函数

将创建群组、添加成员等重复操作抽成私有函数,减少代码冗余:

# 辅助函数:创建群组并批量添加成员
defp create_group_with_members(creator, group_name, members) do
  {:success} = Client.create_group(creator, group_name)
  Enum.each(members, fn member ->
    {:success} = Client.join_group(member, group_name)
  end)
end

完整重构后的测试代码

defmodule GroupTest do
  use ExUnit.Case
  alias Chat.Client, as: Client

  @clients ["lorem", "john doe", "friend24", "tempname2434"]
  @groups ["synergy_squad", "The Rhinos", "guffsuff"]

  setup do
    File.rm_rf!(Chat.Config.get_group_file())
    File.rm_rf!(Chat.Config.get_group_msg_folder())
    File.rm_rf!(Chat.Config.get_group_users_folder())
    {:ok, _} = Chat.System.start_link()

    Enum.each(@clients, fn client ->
      {:ok, _pid} = Client.start_link(client)
      {:success} = Client.signup(client)
    end)

    {:ok, clients: @clients, groups: @groups}
  end

  test "group creation test", %{clients: [lorem, _, _, _], groups: [synergy_squad, _, _]} do
    IO.puts("\n\nGROUP CREATION TEST\n\n")
    {:success} = Client.create_group(lorem, synergy_squad)
    {:failure, :group_already_exists} = Client.create_group(lorem, synergy_squad)
  end

  test "group join test", %{clients: [lorem, john_doe, friend, temp_name], groups: [synergy_squad, rhinos, guffsuff]} do
    IO.puts("\n\nGROUP JOIN TEST\n\n")
    # 自行创建synergy_squad群组
    {:success} = Client.create_group(lorem, synergy_squad)
    
    {:success} = Client.join_group(john_doe, synergy_squad)
    {:failure, :already_a_member} = Client.join_group(john_doe, synergy_squad)
    {:failure, :already_a_member} = Client.join_group(lorem, synergy_squad)
    {:failure, :not_a_group} = Client.join_group(john_doe, "some_random_group_u423428")

    Client.join_group(friend, synergy_squad)

    {:success} = Client.create_group(john_doe, rhinos)
    Client.join_group(lorem, rhinos)
    Client.join_group(friend, rhinos)
    Client.join_group(temp_name, rhinos)

    {:success} = Client.create_group(lorem, guffsuff)
    Client.join_group(temp_name, guffsuff)
  end

  test "send group message test", %{clients: [lorem, _, _, _], groups: [synergy_squad, _, _]} do
    IO.puts("\n\nsend group message test\n\n")
    # 使用辅助函数准备前置环境
    create_group_with_members(lorem, synergy_squad, [])
    Client.send_group_message(lorem, synergy_squad, "@synergy_squad  meet in 30 mins.")
  end

  defp create_group_with_members(creator, group_name, members) do
    {:success} = Client.create_group(creator, group_name)
    Enum.each(members, fn member ->
      {:success} = Client.join_group(member, group_name)
    end)
  end
end

重构后,每个测试都能独立运行,不受执行顺序影响,同时代码复用性和可维护性也得到提升。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 21:17:02