Checking that CancellationTokenSource.Cancel() was invoked with Moq

Solution 1:

Because CancellationTokenSource.Cancel is not virtual you cannot mock it with moq.

You have two options:

Create a wrapper interface:

public interface ICancellationTokenSource
{
    void Cancel();
}

and an implementation which delegates to the wrapped CancellationTokenSource

public class CancellationTokenSourceWrapper : ICancellationTokenSource
{
    private readonly CancellationTokenSource source;

    public CancellationTokenSourceWrapper(CancellationTokenSource source)
    {
        this.source = source;
    }

    public void Cancel() 
    {
        source.Cancel();
    }

}

And use the ICancellationTokenSource as PermanentCancellation then you can create an Mock<ICancellationTokenSource> in your tests:

// arrange

var mockCancellationTokenSource = new Mock<ICancellationTokenSource>();
viewMock.SetupGet(m => m.PermanentCancellation)
        .Returns(mockCancellationTokenSource.Object)

// act

// do something

// assert

mockCancellationTokenSource.Verify(m => m.Cancel());

And use the CancellationTokenSourceWrapper in your production code.

Or use a mocking framework which supports mocking non virtual members like:

  • Microsoft Fakes
  • Typemock isolator (commercial)
  • JustMock (commercial)

Solution 2:

I went a step further and made a factory to create a CancellationTokenManager class that implements the interface. This was because my method has to take CancellationToken and I wanted granular control over .IsCancellationRequested():

My CancellationTokenManagerFactory:

public interface ICancellationTokenManagerFactory
{
  ICancellationTokenManager CreateManager(CancellationToken token);
}

public class CancellationTokenManagerFactory : ICancellationTokenManagerFactory
{
  public ICancellationTokenManager CreateManager(CancellationToken token)
  {
    return new CancellationTokenManager(token);
  }
}

and the manager:

public interface ICancellationTokenManager
{
  bool IsCancellationRequested { get; }

  CancellationToken CancellationToken { get; }
}

public class CancellationTokenManager : ICancellationTokenManager
{
  private readonly CancellationToken _token;

  public CancellationTokenManager(CancellationToken token)
  {
    _token = token;
  }

  public bool IsCancellationRequested
  {
    get
    {
      return _token.IsCancellationRequested;
    }
  }

  public CancellationToken CancellationToken => _token;
}

Then in a class utilizing:

public class MyService
{
  private readonly ICancellationTokenManagerFactory _factory = factory;
  public MyService(ICancellationTokenManagerFactory factory)
  {
    _factory = factory;
  }

  public void StartAsync(CancellationToken token)
  {
    manager = _factory.CreateManager(token);

    //check if cancelled
    if (!manager.IsCancellationRequested())
    }
      // do some work
    }
  }
}

Now if I check cancellation is requested more than once i can mock with different responses each time. Additionally, any interfaces like IHostService can still be utilized because CancellationToken is passed in although it doesn't necessarily matter what is in that token.