Use GitHub Copilot Chat in Visual Studio to solve the .NET Moq drama and migrate to NSubstitute

Use GitHub Copilot Chat in Visual Studio to solve the .NET Moq drama and migrate to NSubstitute

It's now been a few months since the GDPR-drama and data-harvesting around Moq (Stop using Moq as a guinea pig to get feedback on and develop SponsorLink) happened - and the only conclusion that remains is: please migrate away from Moq, as quickly and efficiently as possible.

Migrations

At the time of the drama, regex scripts and manual migrations were quickly recommended to get away from Moq, which sometimes worked more and sometimes less well. However, the larger the code base, the more complex the migration - and the more investment is required to migrate to NSubstitute, for example.

NSubstitute is also the project that has now taken over from Moq as the most popular mocking framework. Please support that project.

GitHub Copilot Chat

The effort of migrating from Moq to NSubstitute can now be significantly reduced thanks to GitHub Copilot Chat. You no longer need regex scripts or manual adjustments - instead, you can use the GitHub Copilot Chat to automate the migration. Automate at least file-by-file, because that's all the GitHub Copilot Chat currently supports.

Sample

I have several hundred files in an existing application that I was able to migrate successfully in just a few steps; for example, this test file:

public class AccountActivateControllerTests
{
    private readonly Mock<IEventEngineCore> _eventEngineMoq = new();
    private readonly Mock<IPortalRouter> _prMock = new();
    private readonly Mock<IIdentityContextAccessor> _icMoq = new();
    private readonly AccountActivateController _controller;

    public AccountActivateControllerTests()
    {
        _controller = new AccountActivateController(
            _eventEngineMoq.Object, _icMoq.Object, _prMock.Object);
    }

    [Fact]
    public void Activate_GET_Without_Passed_ActivationKey()
    {
        // setup
        _icMoq.Setup(x => x.GetUserIdOrThrow()).Returns(new UserId(1));

        // arrange
        AccountActivateController controller = _controller;

        // act
        IActionResult activateResult = controller.Activate(key: null);
        ViewResult viewResult = Assert.IsAssignableFrom<ViewResult>(activateResult);

        // assert
        viewResult.ViewName.Should().Be("~/Areas/Account/Views/Account/Activate.cshtml");

        // verify
        _eventEngineMoq
            .Verify(ed =>
                ed.Send(It.IsAny<UserAccountTryActivateByKeyCommand>(), CancellationToken.None),
                Times.Never);
    }

I selected this code in the VS editor and prompted convert it from moq to NSubstitute in the Copilot Chat. A few seconds later, GitHub Copilot replaced all moq snippets with NSubstitute actions:

public class AccountActivateControllerTests
{
    private readonly IEventEngineCore _eventEngine = Substitute.For<IEventEngineCore>();
    private readonly IPortalRouter _pr = Substitute.For<IPortalRouter>();
    private readonly IIdentityContextAccessor _ic = Substitute.For<IIdentityContextAccessor>();
    private readonly AccountActivateController _controller;

    public AccountActivateControllerTests()
    {
        _controller = new AccountActivateController(_eventEngine, _ic, _pr);
    }

    [Fact]
    public void Activate_GET_Without_Passed_ActivationKey()
    {
        // setup
        _ic.GetUserIdOrThrow().Returns(new UserId(1));

        // arrange
        AccountActivateController controller = _controller;

        // act
        IActionResult activateResult = controller.Activate(key: null);
        ViewResult viewResult = Assert.IsAssignableFrom<ViewResult>(activateResult);

        // assert
        viewResult.ViewName.Should().Be("~/Areas/Account/Views/Account/Activate.cshtml");

        // verify
        _eventEngine
            .DidNotReceive()
            .Send(Arg.Any<UserAccountTryActivateByKeyCommand>(), CancellationToken.None);
    }

GitHub Copilot is simply brilliant! Goodbye Moq. Hello NSubstitute ♥