I recently wrote an asynchronous call method that calls an external long asynchronous method, so I decided to pass a CancellationToken, which allows to cancel. The method can be called at the same time.
The implementation combined the exponential deferral and timeout methods described in Stephen Cleary 's book in the C # cookbook as follows;
/// <summary> /// Sets bar /// </summary> /// <param name="cancellationToken">The cancellation token that cancels the operation</param> /// <returns>A <see cref="Task"/> representing the task of setting bar value</returns> /// <exception cref="OperationCanceledException">Is thrown when the task is cancelled via <paramref name="cancellationToken"/></exception> /// <exception cref="TimeoutException">Is thrown when unable to get bar value due to time out</exception> public async Task FooAsync(CancellationToken cancellationToken) { TimeSpan delay = TimeSpan.FromMilliseconds(250); for (int i = 0; i < RetryLimit; i++) { if (i != 0) { await Task.Delay(delay, cancellationToken); delay += delay; // Exponential backoff } await semaphoreSlim.WaitAsync(cancellationToken); // Critical section is introduced for long running operation to prevent race condition using (CancellationTokenSource cancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken)) { cancellationTokenSource.CancelAfter(TimeSpan.FromMilliseconds(Timeout)); CancellationToken linkedCancellationToken = cancellationTokenSource.Token; try { cancellationToken.ThrowIfCancellationRequested(); bar = await barService.GetBarAsync(barId, linkedCancellationToken).ConfigureAwait(false); break; } catch (OperationCanceledException) when (!cancellationToken.IsCancellationRequested) { if (i == RetryLimit - 1) { throw new TimeoutException("Unable to get bar, operation timed out!"); } // Otherwise, exception is ignored. Will give it another try } finally { semaphoreSlim.Release(); } } } }
I wonder if I should write unit test, which explicitly states that the barService.GetBarAsync() internal task is canceled whenever FooAsync() is canceled. If so, how to implement it cleanly?
In addition, whether to ignore implementation details and just check what the client / caller is, as described in the method summary (line updated, operationCanceledException cancellation triggers, TimeoutException timeout TimeoutException ).
If not, should I get my feet wet and start doing unit tests for the following cases:
- Testing is thread safe (monitor is only purchased one thread at a time)
- Repeat mechanism testing
- Server testing not flooded
- Testing, perhaps even a regular exception applies to the caller
source share