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

Playwright集成测试因事务锁定无法完成问题排查

问题分析与解决方案

核心误区:Playwright测试的跨进程特性

你当前的问题根源在于Playwright是端到端(E2E)测试框架,测试代码与被测系统(SUT)运行在两个独立进程中——测试代码在本地进程执行,而SUT是独立启动的Web服务进程。这意味着:

  • 测试进程中创建的DbContext、开启的事务,完全无法被SUT进程中的DbContext共享,因为事务是绑定到数据库连接实例的,跨进程无法传递连接或事务对象。
  • 即使你在Startup中配置强制使用同一数据库实例,也只是让SUT进程内的DbContext共享连接,和测试进程的DbContext没有任何关联。

当前代码的具体问题

  1. DbContext基类的transaction变量来源不明:构造函数中判断transaction != null,但代码里没有体现这个变量是如何传递到SUT的DbContext实例中的——跨进程下根本无法传递,所以这个逻辑本质上不生效。
  2. ReadUncommitted隔离级别无法解决锁问题:该级别只是允许读取未提交的数据,但事务内的写操作依然会加排他锁,SUT进程访问被修改的数据时依然会被阻塞。

可行解决方案

方案1:切换为进程内集成测试(放弃Playwright)

如果你的测试目标是验证业务逻辑与数据库的交互,而非前端UI流程,可以改为进程内集成测试:

  • 直接在测试代码中实例化服务层/控制器,不启动独立Web服务。
  • 此时测试代码与SUT共享同一个进程和依赖注入容器,就可以通过共享事务的方式实现测试数据的隔离与回滚:
    • 在测试Scope中开启事务,并将事务实例注册到容器。
    • 所有DbContext构造时从容器中获取事务并绑定。
    • 测试结束后统一回滚事务。

方案2:适配Playwright的E2E测试数据隔离

如果必须保留Playwright的UI测试,需要换一种数据隔离方式:

  • 每个测试使用独立数据库:测试开始前创建全新的测试数据库(基于模板库克隆),测试结束后销毁。这种方式完全隔离,不会有锁问题。
  • 测试前后执行SQL脚本:测试开始前运行清理脚本+测试数据插入脚本;测试结束后运行清理脚本(比如删除测试数据、重置序列等)。注意脚本要避免长时间锁表,尽量使用批量操作。
  • 启用SQL Server快照隔离:在数据库上开启快照隔离,这样SUT读取数据时不会被测试进程的事务阻塞,但需要注意快照隔离的性能开销和版本存储问题。

针对同进程场景的代码修正(如果切换为进程内测试)

如果改为进程内集成测试,需要修正事务传递逻辑:

  1. 修改测试的getContext方法,将事务注册到当前Scope:
public T getContext<T>() where T : DbContext {
    var context = Scope.ServiceProvider.GetRequiredService<T>();
    Contexts.Add(context);
    // 开启事务后,将事务实例注册到Scope
    var trans = context.Database.BeginTransaction(System.Data.IsolationLevel.ReadCommitted);
    Scope.ServiceProvider.GetRequiredService<ITestTransactionHolder>().SetTransaction(trans);
    Transactions.Add(trans);
    return context;
}
  1. 定义一个事务持有者服务:
public interface ITestTransactionHolder {
    IDbContextTransaction Transaction { get; set; }
    void SetTransaction(IDbContextTransaction trans);
}

public class TestTransactionHolder : ITestTransactionHolder {
    public IDbContextTransaction Transaction { get; set; }
    public void SetTransaction(IDbContextTransaction trans) => Transaction = trans;
}
  1. 在测试的依赖注入配置中注册该服务为Scoped:
services.AddScoped<ITestTransactionHolder, TestTransactionHolder>();
  1. 修改DbContext基类,从容器中获取事务:
public DBContextBase(DbContextOptions options, ITestTransactionHolder transactionHolder) : base(options) {
    if (transactionHolder?.Transaction != null) {
        Database.UseTransaction(transactionHolder.Transaction.GetDbTransaction());
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 12:26:04