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

Moq测试OperationCanceledException场景失败,异常未被捕获求排查

问题描述

我用Moq 4.18.2框架做单元测试,RtspClient的ConnectAsync方法可能抛出OperationCanceledException,因此尝试测试该场景。但测试代码直接抛出了System.OperationCanceledException: The operation was canceled.异常,且业务代码中的catch (OperationCanceledException)代码块对应的事件没触发,加上缺失的await后测试仍然失败,请问问题出在哪?


RTSP接口定义

public interface IRtspClient : IDisposable
{
    event EventHandler<RawFrame> FrameReceived;

    Task ConnectAsync(CancellationToken token);
    Task ReceiveAsync(CancellationToken token);
}

使用IRtspClient的业务方法

public async Task ConnectAsync(CancellationToken token = default)
{
    try
    {
        await _rtspClient.ConnectAsync(token).ConfigureAwait(false);
        OnConnected();
    }
    catch (OperationCanceledException ex)
    {
        OnConnectAttemptCanceled(ex);
        throw;
    }
    catch(Exception ex)
    {
        OnFailedToConnect(ex);
        throw;
    }
}

原测试代码

[TestMethod]
public async Task ConnectAsync_Canceled()
{
    var expectedCanceledTaskStatus = true;

    var tcs = new CancellationTokenSource();
    tcs.Cancel();
    var rtspClient = new Mock<IRtspClient>();
    rtspClient
        .Setup(_ => _.ConnectAsync(It.IsAny<CancellationToken>()))
        .Returns(Task.FromException<OperationCanceledException>(new OperationCanceledException()));

    var actualCanceledTaskStatus = false;
    var camera = new MyCamera(rtspClient.Object);
    camera.FailedToConnect += () => actualCanceledTaskStatus = true;
    await camera.ConnectAsync(tcs.Token);

    Assert.AreEqual(expectedCanceledTaskStatus, actualCanceledTaskStatus);
}

问题分析与修正

1. 事件订阅错误

业务代码中,OperationCanceledException会被第一个catch块捕获,触发的是OnConnectAttemptCanceled事件,但原测试中订阅的是FailedToConnect事件,这导致actualCanceledTaskStatus永远不会被设为true,断言必然失败。

2. 未处理测试中的抛出异常

业务代码在捕获OperationCanceledException后会重新抛出,原测试中直接await camera.ConnectAsync(...)会导致测试因未捕获异常而崩溃,无法执行后续断言。

3. 模拟异常的优化(可选)

用Moq的ThrowsAsync方法模拟抛出异常比Returns(Task.FromException<...>)更简洁,且让异常关联传入的CancellationToken,更贴合实际场景。


修正后的测试代码

[TestMethod]
public async Task ConnectAsync_Canceled()
{
    var expectedCanceledTaskStatus = true;

    var tcs = new CancellationTokenSource();
    tcs.Cancel();
    var rtspClient = new Mock<IRtspClient>();
    // 用ThrowsAsync模拟抛出关联取消令牌的OperationCanceledException
    rtspClient
        .Setup(_ => _.ConnectAsync(It.IsAny<CancellationToken>()))
        .ThrowsAsync(new OperationCanceledException(tcs.Token));

    var actualCanceledTaskStatus = false;
    var camera = new MyCamera(rtspClient.Object);
    // 订阅正确的ConnectAttemptCanceled事件
    camera.ConnectAttemptCanceled += (ex) => actualCanceledTaskStatus = true;

    // 验证方法会抛出预期异常,同时避免测试崩溃
    await Assert.ThrowsExceptionAsync<OperationCanceledException>(() => camera.ConnectAsync(tcs.Token));

    Assert.AreEqual(expectedCanceledTaskStatus, actualCanceledTaskStatus);
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 12:36:24