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
相关产品推荐
相关产品推荐

