
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:
1public class AccountActivateControllerTests
2{
3 private readonly Mock<IEventEngineCore> _eventEngineMoq = new();
4 private readonly Mock<IPortalRouter> _prMock = new();
5 private readonly Mock<IIdentityContextAccessor> _icMoq = new();
6 private readonly AccountActivateController _controller;
7
8 public AccountActivateControllerTests()
9 {
10 _controller = new AccountActivateController(
11 _eventEngineMoq.Object, _icMoq.Object, _prMock.Object);
12 }
13
14 [Fact]
15 public void Activate_GET_Without_Passed_ActivationKey()
16 {
17 // setup
18 _icMoq.Setup(x => x.GetUserIdOrThrow()).Returns(new UserId(1));
19
20 // arrange
21 AccountActivateController controller = _controller;
22
23 // act
24 IActionResult activateResult = controller.Activate(key: null);
25 ViewResult viewResult = Assert.IsAssignableFrom<ViewResult>(activateResult);
26
27 // assert
28 viewResult.ViewName.Should().Be("~/Areas/Account/Views/Account/Activate.cshtml");
29
30 // verify
31 _eventEngineMoq
32 .Verify(ed =>
33 ed.Send(It.IsAny<UserAccountTryActivateByKeyCommand>(), CancellationToken.None),
34 Times.Never);
35 }
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:
1public class AccountActivateControllerTests
2{
3 private readonly IEventEngineCore _eventEngine = Substitute.For<IEventEngineCore>();
4 private readonly IPortalRouter _pr = Substitute.For<IPortalRouter>();
5 private readonly IIdentityContextAccessor _ic = Substitute.For<IIdentityContextAccessor>();
6 private readonly AccountActivateController _controller;
7
8 public AccountActivateControllerTests()
9 {
10 _controller = new AccountActivateController(_eventEngine, _ic, _pr);
11 }
12
13 [Fact]
14 public void Activate_GET_Without_Passed_ActivationKey()
15 {
16 // setup
17 _ic.GetUserIdOrThrow().Returns(new UserId(1));
18
19 // arrange
20 AccountActivateController controller = _controller;
21
22 // act
23 IActionResult activateResult = controller.Activate(key: null);
24 ViewResult viewResult = Assert.IsAssignableFrom<ViewResult>(activateResult);
25
26 // assert
27 viewResult.ViewName.Should().Be("~/Areas/Account/Views/Account/Activate.cshtml");
28
29 // verify
30 _eventEngine
31 .DidNotReceive()
32 .Send(Arg.Any<UserAccountTryActivateByKeyCommand>(), CancellationToken.None);
33 }
GitHub Copilot is simply brilliant! Goodbye Moq. Hello NSubstitute ♥
Related articles

Oct 05, 2026 - 12 min read
Problem Details in ASP.NET Core and .NET 11
builder.Services.AddProblemDetails() registers infrastructure. It does not create an error contract. An ASP.NET Core request can fail during …

Sep 21, 2026 - 11 min read
Resilience in .NET with Microsoft.Extensions.Resilience
Retries are the most visible resilience strategy and the easiest one to misuse. Repeating a failing call can recover from a short network …

Sep 14, 2026 - 10 min read
What Matters in ASP.NET Core 11
ASP.NET Core 11 has no single feature that fundamentally changes how web applications are built. That is not a criticism. Mature frameworks …
Let's Work Together
Looking for an experienced Platform Architect or Engineer for your next project? Whether it's cloud migration, platform modernization or building new solutions from scratch - I'm here to help you succeed.
