简体   繁体   中英

Imitation of OperationCanceledException scenario with unit-test fails

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?

  • You need to await the returned Task inside the try{} block - otherwise synchronous control will immediately leave the try{} block.
  • Exceptions thrown inside an anonymous function (or local function, or lambda method, or ye olde school delegate() local) will not be caught by the catch .
  • Also, CancellationTokenSource is IDisposable , so you should change your ConnectAsync_Canceled test to wrap it in a using() 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.
    }
}

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);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM