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

为何选择运行单个测试时,集合中其他测试也会执行?

单个测试运行时Collection Fixture多次执行导致其他测试数据异常的问题

问题背景

我有一个包含4个测试的类,该类归属带有共享DatabaseFixture的测试集合。在Visual Studio Test Explorer中执行类内单个测试时,测试本身能通过,但查看数据库状态发现其他测试也在运行——SQL Profiler证实了这一点。

SQL Profiler还显示,选择运行单个测试时,DatabaseFixture的构造函数会被多次执行。右键代码编辑器中的单个测试选择运行,结果一致。

我尝试将该类从集合中移除,改为在每个测试方法内实例化DatabaseFixture,此时单个测试运行后的数据库状态符合预期,但我不理解为何移除集合会有这种差异。按我的理解,Collection Fixture的核心作用就是为集合内所有测试仅运行一次。

由于DatabaseFixture构造函数会加载部分数据,我更希望保留集合模式——为每个测试实例化Fixture速度慢,且每个测试方法需要大量配置工作。

简化示例代码

DatabaseFixture实现

public class DatabaseFixture : IDisposable
{
    internal IDbConnection Db;
    internal IConfiguration Configuration;
    internal OrmLiteConnectionFactory dbFactory;

    public DatabaseFixture()
    {
        Configuration = new ConfigurationBuilder().AddJsonFile("appsettings.json").Build();
        var connectionString = Configuration.GetConnectionString(name: "Connection");

        dbFactory
           = new OrmLiteConnectionFactory(connectionString, SqlServerDialect.Provider, false);

        Db = dbFactory.Open();

        Db.ExecuteNonQuery("DELETE FROM TestTable");

    }
    public void Dispose()
    {
        
    }
}

测试集合定义

[CollectionDefinition("TestCollection")]
public class TestCollection : ICollectionFixture<DatabaseFixture>
{
}

测试类

[Collection("TestCollection")]
public class TestClass
{
    DatabaseFixture dbFixture;
    public TestClass(DatabaseFixture database)
    {
        dbFixture = database;
    }

    [Fact]
    public void Test1()
    {
        dbFixture.Db.Insert<TestTable>(new TestTable { Value = DateTime.Now.ToString(), Method = "TEST 1" });
    }

    [Fact]
    public void Test2()
    {
        dbFixture.Db.Insert<TestTable>(new TestTable { Value = DateTime.Now.ToString(), Method = "TEST 2" });

    }
}

现象对比

  • 仅执行Test1时,表中出现两条数据(Test1和Test2的插入记录);
  • 将TestClass修改为以下代码后仅执行Test1,表中仅出现一条预期数据:
public class TestClass
{
    DatabaseFixture dbFixture;

    [Fact]
    public void Test1()
    {
        dbFixture = new();
        dbFixture.Db.Insert<TestTable>(new TestTable { Value = DateTime.Now.ToString(), Method = "TEST 1" });
    }

    [Fact]
    public void Test2()
    {
        dbFixture = new();
        dbFixture.Db.Insert<TestTable>(new TestTable { Value = DateTime.Now.ToString(), Method = "TEST 2" });

    }
}

原因分析

xUnit.net的默认行为是并行执行测试集合内的测试,即使你选择运行单个测试,测试运行器可能会因为并行机制、测试发现流程等原因,实例化多个DatabaseFixture并执行同一类中的其他测试。另外,共享的数据库连接会导致不同测试的操作互相干扰——Fixture构造时仅执行一次数据清理,后续所有测试共用同一个连接,若其他测试被并行触发,就会向数据库插入额外数据。

Collection Fixture的确设计为集合内共享单实例,但并行执行会打破这个预期:测试运行器可能为不同的测试线程创建多个Fixture实例,尤其是当测试类被并行实例化时。

解决方案

1. 禁用测试并行执行

在项目根目录创建xunit.runner.json文件,添加以下配置:

{
  "parallelizeTestCollections": false,
  "parallelizeAssembly": false
}

同时在Visual Studio的Test Explorer设置中,取消勾选“并行运行测试”选项。这能确保Fixture仅被实例化一次,且测试串行执行,避免互相干扰。

2. 为每个测试提供隔离的数据库连接

修改DatabaseFixture,不再保持长期打开的连接,而是为每个测试提供新连接并重置数据:

public class DatabaseFixture : IDisposable
{
    internal IConfiguration Configuration;
    internal OrmLiteConnectionFactory dbFactory;

    public DatabaseFixture()
    {
        Configuration = new ConfigurationBuilder().AddJsonFile("appsettings.json").Build();
        var connectionString = Configuration.GetConnectionString(name: "Connection");

        dbFactory = new OrmLiteConnectionFactory(connectionString, SqlServerDialect.Provider, false);
    }

    // 为每个测试获取新连接并清理数据
    public IDbConnection GetDbConnection()
    {
        var db = dbFactory.Open();
        db.ExecuteNonQuery("DELETE FROM TestTable");
        return db;
    }

    public void Dispose()
    {
        // 清理连接工厂资源
    }
}

修改测试类,每个测试实例使用独立连接:

[Collection("TestCollection")]
public class TestClass : IDisposable
{
    DatabaseFixture dbFixture;
    IDbConnection db;

    public TestClass(DatabaseFixture database)
    {
        dbFixture = database;
        db = dbFixture.GetDbConnection();
    }

    [Fact]
    public void Test1()
    {
        db.Insert<TestTable>(new TestTable { Value = DateTime.Now.ToString(), Method = "TEST 1" });
    }

    [Fact]
    public void Test2()
    {
        db.Insert<TestTable>(new TestTable { Value = DateTime.Now.ToString(), Method = "TEST 2" });
    }

    public void Dispose()
    {
        db.Dispose();
    }
}

这种方式既保留了Collection Fixture的共享配置优势,又保证了每个测试的数据隔离,即使并行执行也不会出现数据干扰。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 21:01:05