Simulating CancellationToken.IsCancellationRequested when unit testing

The best you can do without changing your code is cancelling after a specific amount of time. The CancellationTokenSource.CancelAfter() method makes this easy:

[TestCase]
public async System.Threading.Tasks.Task Run_ShallAlwaysReleaseResources()
{

  // Signal cancellation after 5 seconds
  var cts = new TestCancellationTokenSource();
  cts.CancelAfter(TimeSpan.FromSeconds(5));

  // Act
  await domainStateSerializationWorker.Run(cts.Token);  

  // Assert
  // assert that resource release has been called
}

The way your code is written (checking IsCancellationRequested only once per iteration) means that the cancellation will happen after some number of complete iterations. It just won't be the same number each time.

If you want to cancel after a specific number of iterations, then your only option is to modify your code to keep track of how many iterations have happened.

I thought I might be able to create a new class that inherits from CancellationTokenSource to keep track of how many times IsCancellationRequested has been tested, but it's just not possible to do.


This depends on what is running within Run. If there is some injected dependency

For example

public interface IDependency {
    Task DoSomething();
}

public class Worker {
    private readonly IDependency dependency;

    public Worker(IDependency dependency) {
        this.dependency = dependency;
    }

    public async Task Run(CancellationToken cancellationToken) {
        while (!cancellationToken.IsCancellationRequested) {
            try {
                // do something like claim a resource
                await dependency.DoSomething();
            } catch (Exception e) {
                // catch exceptions and print to the log
            } finally {
                // release the resource
            }
        }
    }
}

Then that can be mocked and monitored to count how many times some member has been invoked.

[TestClass]
public class WorkerTests {
    [TestMethod]
    public async Task Sohuld_Cancel_Run() {
        //Arrange
        int expectedCount = 5;
        int count = 0;
        CancellationTokenSource cts = new CancellationTokenSource();
        var mock = new Mock<IDependency>();
        mock.Setup(_ => _.DoSomething())
            .Callback(() => {
                count++;
                if (count == expectedCount)
                    cts.Cancel();
            })
            .Returns(() => Task.FromResult<object>(null));

        var worker = new Worker(mock.Object);

        //Act
        await worker.Run(cts.Token);

        //Assert
        mock.Verify(_ => _.DoSomething(), Times.Exactly(expectedCount));
    }
}