在Cabbage中隔离Gherkin场景:Elixir测试隔离方案问询
如何用Cabbage实现单个Gherkin场景的隔离
我刚好之前处理过类似的Cabbage场景隔离问题,你的痛点确实很典型——默认的setup太细(每个步骤都跑),setup_all又太粗(整个feature才跑一次)。其实Cabbage本身就提供了场景级的钩子,另外还有备选方案,给你详细说说:
方法1:用Cabbage原生的场景级钩子(最推荐)
Cabbage内置了before_scenario和after_scenario函数,它们会在每个场景的所有步骤执行前/后各运行一次,完全匹配你要的单个场景隔离需求。
举个实际的代码例子,假设你用Ecto做持久化:
defmodule UserManagementFeatureTest do use Cabbage.Feature, async: false # 持久化操作别开异步,避免竞争 # 每个场景执行前重置持久化状态 def before_scenario(_scenario_details, _tags) do # 这里可以是清空数据库表,或者用Ecto Sandbox启动事务 MyApp.Repo.delete_all(MyApp.User) :ok end # 可选:场景结束后的清理(比如关闭外部连接) def after_scenario(_scenario_details, _tags) do :ok end # 你的Gherkin步骤定义... given ~r/系统中没有用户/, _vars, _state do assert MyApp.Repo.all(MyApp.User) == [] {:ok, %{}} end when ~r/我创建一个名为"([^"]+)"的用户/, [name], _state do {:ok, user} = MyApp.create_user(%{name: name}) {:ok, %{user: user}} end then ~r/系统中应该有一个名为"([^"]+)"的用户/, [name], %{user: user} do assert user.name == name assert MyApp.Repo.get_by(MyApp.User, name: name) != nil end end
这个方法的好处是完全贴合Cabbage的设计,代码清晰,不需要额外的hack。
如果用Ecto的话,更高效的方式是用Sandbox事务隔离,代替删除数据:
def before_scenario(_scenario, _tags) do # 启动独立事务,场景结束后自动回滚 {:ok, pid} = Ecto.Adapters.SQL.Sandbox.start_owner!(MyApp.Repo, shared: true) on_exit(fn -> Ecto.Adapters.SQL.Sandbox.stop_owner(pid) end) :ok end
这样比每次删除数据快很多,而且不会污染测试数据库。
方法2:结合ExUnit Tags的备选方案
如果因为某些限制没法用原生钩子,你可以给Gherkin场景加标签,然后在setup里判断是否是新场景的第一步。
先给场景加标签:
@isolated-scenario Scenario: 创建用户并验证 Given 系统中没有用户 When 我创建名为"Bob"的用户 Then 系统存在名为"Bob"的用户 @isolated-scenario Scenario: 删除用户并验证 Given 系统中有一个名为"Bob"的用户 When 我删除名为"Bob"的用户 Then 系统中没有名为"Bob"的用户
然后在测试模块里用进程存储跟踪当前场景:
defmodule UserManagementFeatureTest do use Cabbage.Feature, async: false setup %{scenario: scenario} do # 用场景ID判断是否是新场景 current_scenario_id = Process.get(:active_scenario_id) if current_scenario_id != scenario.id do # 重置持久化 MyApp.Repo.delete_all(MyApp.User) Process.put(:active_scenario_id, scenario.id) end :ok end # 步骤定义... end
这个方法不如原生钩子优雅,但在特殊场景下可以应急。
关键注意事项
- 一定要把
async: false加上:持久化操作是有状态的,异步测试会导致多个场景同时修改数据,隔离完全失效。 - 优先用原生钩子:Cabbage的
before_scenario是官方设计用来处理场景级初始化的,比自己造轮子靠谱得多。
内容的提问来源于stack exchange,提问作者aef
相关产品推荐
相关产品推荐

