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

运行XUnit测试类时出现DbUpdateConcurrencyException错误的修复方法

问题分析与解决方案

你遇到的问题核心是测试之间的隔离性不足:运行整个测试类时,XUnit默认会并行执行测试方法,或测试方法共享了数据库上下文/测试数据,导致DeleteWithCorrectDataTest执行时,目标记录已被其他测试提前删除。EF Core执行删除操作时检测到并发冲突(上下文预期删除1行,但数据库中无对应记录),从而抛出DbUpdateConcurrencyException。

修复方案:

1. 确保每个测试使用独立的数据库上下文

不要在测试类中共享同一个DbContext实例,每个测试方法都应创建全新的上下文。例如:

public class DeleteTests
{
    private readonly AppDbContext _context;

    public DeleteTests()
    {
        // 每次生成唯一数据库名,确保测试数据完全隔离
        var options = new DbContextOptionsBuilder<AppDbContext>()
            .UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString())
            .Options;
        _context = new AppDbContext(options);
    }

    [Fact]
    public async Task DeleteWithCorrectDataTest()
    {
        // Arrange:单独插入当前测试需要的记录
        var testWorkType = new WorkType { Id = 1, Name = "Test" };
        _context.WorkTypes.Add(testWorkType);
        await _context.SaveChangesAsync();

        var repository = new WorkTypeRepository(_context);

        // Act
        await repository.DeleteAsync(1);

        // Assert
        var deletedEntity = await _context.WorkTypes.FindAsync(1);
        Assert.Null(deletedEntity);
    }
}

2. 为每个测试准备独立的测试数据

每个测试方法的Arrange步骤都要单独插入所需的测试数据,不要依赖全局初始化的共享数据,彻底避免不同测试操作同一条记录的情况。

3. 优化Delete方法的实现

如果你的Delete方法是通过Attach实体再删除的方式(如下),极易触发并发异常:

// 不推荐的实现
public async Task DeleteAsync(int id)
{
    var entity = new WorkType { Id = id };
    _context.Attach(entity);
    _context.Remove(entity);
    await _context.SaveChangesAsync();
}

改为先查询实体再删除,或使用ExecuteDeleteAsync直接执行SQL删除,避免上下文跟踪导致的并发检测:

// 推荐实现1:先查询再删除
public async Task<bool> DeleteAsync(int id)
{
    var entity = await _context.WorkTypes.FindAsync(id);
    if (entity == null)
        return false;

    _context.Remove(entity);
    await _context.SaveChangesAsync();
    return true;
}

// 推荐实现2:直接执行删除
public async Task<int> DeleteAsync(int id)
{
    return await _context.WorkTypes
        .Where(w => w.Id == id)
        .ExecuteDeleteAsync();
}

测试时可通过断言返回值(true或1)验证删除是否成功。

4. 禁用测试并行执行(临时排查方案)

如果暂时无法完成测试隔离,可在测试项目的xunit.runner.json中禁用并行执行,确保测试类中的方法串行运行:

{
  "parallelizeTestCollections": false
}

注意:这只是临时排查手段,长期来看仍需做好测试隔离,避免测试间的依赖。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 06:11:15