I use Moq 4.18.2 framework for my tests.
The RtspClient
might throw an OperationCanceledException
from ConnectAsync
. So, I try to test this scenario. My test below throws an exception System.OperationCanceledException: The operation was canceled.
and the catch (OperationCanceledException)
never gets executed. What am I doing wrong here?
RTSP
public interface IRtspClient : IDisposable
{
event EventHandler<RawFrame> FrameReceived;
Task ConnectAsync(CancellationToken token);
Task ReceiveAsync(CancellationToken token);
}
Method that uses 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;
}
}
Test
[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);
}
UPDATE
Added missing await
as suggested by @Dai, but my test still fails. Can anyone take a look at the test code?
CodePudding user response:
- You need to
await
the returnedTask
inside thetry{}
block - otherwise synchronous control will immediately leave thetry{}
block. - Exceptions thrown inside an anonymous function (or local function, or lambda method, or ye olde school
delegate()
local) will not be caught by thecatch
. - Also,
CancellationTokenSource
isIDisposable
, so you should change yourConnectAsync_Canceled
test to wrap it in ausing()
block. - Also, don't swallow exceptions - so my code below captures both exceptions for possible investigation.
Change your code to this:
public async Task ConnectAsync( CancellationToken cancellationToken = default )
{
try
{
await this.rtspClient.ConnectAsync(cancellationToken ).ConfigureAwait(false);
this.OnConnected();
}
catch( OperationCanceledException cEx )
{
this.OnConnectAttemptCanceled( cEx );
throw; // Re-throw so the `Task` representing *this method* (`ConnectAsync`) will report as Cancelled rather than Succeeded.
}
catch( Exception ex )
{
this.OnFailedToConnect( ex );
throw; // Re-throw so the `Task` representing *this method* (`ConnectAsync`) will report as Failed rather than Succeeded.
}
}
CodePudding user response:
I found my mistake (in addition to what @Dai noticed). I should have either put await camera.ConnectAsync
from my test in try-catch
or used Assert.ThrowsExceptionAsync
. I chose the latter. Here is the working test:
[TestMethod]
public async Task ConnectAsync_Canceled()
{
var expectedTaskCanceledStatus = true;
var rtspClient = new Mock<IRtspClient>();
rtspClient
.Setup(_ => _.ConnectAsync(default(CancellationToken)))
.Returns(Task.FromException(new OperationCanceledException()));
var actualTaskCanceledStatus = false;
var camera = new MyCamera(rtspClient.Object);
camera.ConnectAttemptCanceled = () => actualTaskCanceledStatus = true;
await Assert.ThrowsExceptionAsync<OperationCanceledException>(async () => await camera.ConnectAsync());
Assert.AreEqual(expectedTaskCanceledStatus, actualTaskCanceledStatus);
}