
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

Mar 10, 2026 · 15 min read
.NET NuGet Trusted Publishing with GitHub Actions
Publishing NuGet packages has traditionally required one uncomfortable compromise: a long-lived API key had to exist somewhere in the …

Mar 09, 2026 · 7 min read
C# 15 Unions: Unions are finally in .NET
After many years of workarounds, design discussions and library-level substitutes, unions are finally becoming a first-class part of C#. The …

Mar 02, 2026 · 19 min read
Unio: High-Performance Discriminated Unions for C#
C# is a powerful language, but there is one road it has not yet fully paved: native discriminated union types. Developers have been working …
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.

Comments