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
相关产品推荐
相关产品推荐

