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

如何为NUnit测试设置默认超时?Azure DevOps流水线超时问题求助

问题描述

Aborting test run: test run timeout of 2700000 milliseconds exceeded

Azure DevOps流水线时常因上述超时提示失败,部分测试耗时超出预期但无法定位具体测试。需求如下:

  • 为所有NUnit测试设置2分钟默认超时,无需逐个添加Timeout属性
  • 新增测试自动应用该超时规则
  • 超时后立即标记测试失败

调研发现NUnit原生不支持全局默认超时,尝试过在SetUp/TearDown中用System.Timers.Timer的方案,但超2分钟后无法立即触发失败,代码如下:

System.Timers.Timer timer;
public System.Timers.Timer Timer
{
    get
    {
        if (timer == null)
        {
            timer = new System.Timers.Timer();
            timer.Interval = 120000;
            timer.Elapsed += Timer_Elapsed;
        }
        return timer;
    }
}

void Timer_Elapsed(object sender, ElapsedEventArgs e)
{
    Timer.Stop();
    Assert.Fail("Timeout");
}

[SetUp]
public void Setup()
{
    Timer.Start();
}

[TearDown]
public void TernDown()
{
    Timer.Stop();
}
解决方案

方案1:全局超时特性(推荐)

自定义TestActionAttribute实现全局注入,无需修改现有测试代码,程序集级别生效后所有测试自动继承超时规则:

using NUnit.Framework;
using NUnit.Framework.Interfaces;
using System;
using System.Threading;

[AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)]
public class GlobalTimeoutAttribute : TestActionAttribute
{
    private readonly int _timeoutMs;
    private CancellationTokenSource _cts;

    public GlobalTimeoutAttribute(int timeoutMs = 120000)
    {
        _timeoutMs = timeoutMs;
    }

    public override void BeforeTest(ITest test)
    {
        _cts = new CancellationTokenSource(_timeoutMs);
        _cts.Token.Register(() =>
        {
            throw new TestTimeoutException($"测试超时,已超过{_timeoutMs/1000}秒");
        });
    }

    public override void AfterTest(ITest test)
    {
        _cts?.Dispose();
    }

    public override ActionTargets Targets => ActionTargets.Test;
}

public class TestTimeoutException : Exception
{
    public TestTimeoutException(string message) : base(message) { }
}

在AssemblyInfo.cs中添加程序集标记,全局启用:

[assembly: GlobalTimeout(120000)] // 2分钟超时

方案2:基类封装超时逻辑

创建测试基类,所有测试类继承该基类,同步/异步测试都支持:

using NUnit.Framework;
using System;
using System.Threading;
using System.Threading.Tasks;

public class TestBase
{
    protected void RunWithTimeout(Action testAction, int timeoutMs = 120000)
    {
        using var cts = new CancellationTokenSource(timeoutMs);
        try
        {
            Task.Run(testAction, cts.Token).Wait(cts.Token);
        }
        catch (AggregateException ex)
        {
            if (ex.InnerException is OperationCanceledException)
            {
                Assert.Fail($"测试超时,已超过{timeoutMs/1000}秒");
            }
            throw;
        }
    }

    protected async Task RunWithTimeout(Func<Task> testAction, int timeoutMs = 120000)
    {
        using var cts = new CancellationTokenSource(timeoutMs);
        try
        {
            await Task.Run(testAction, cts.Token);
        }
        catch (OperationCanceledException)
        {
            Assert.Fail($"测试超时,已超过{timeoutMs/1000}秒");
        }
    }
}

// 测试类示例
[TestFixture]
public class MyTests : TestBase
{
    [Test]
    public void SyncLongTest()
    {
        RunWithTimeout(() =>
        {
            Thread.Sleep(130000); // 模拟超时场景
        });
    }

    [Test]
    public async Task AsyncLongTest()
    {
        await RunWithTimeout(async () =>
        {
            await Task.Delay(130000); // 模拟超时场景
        });
    }
}
方案对比
  • 方案1:零侵入,程序集级别全局生效,新增测试自动继承,超时后立即抛出异常终止测试,能准确定位超时测试,是最优解。
  • 方案2:需要测试类继承基类,同步/异步测试都兼容,灵活性较强但有一定侵入性。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.21 22:36:20