Context
All test projects currently use Moq for mocking. TechHubApiClient is intentionally non-sealed to support Moq's proxy generation. Moq had the SponsorLink controversy and requires non-sealed classes.
NSubstitute has a cleaner API and works directly with interfaces (no need for non-sealed classes).
Target State
Replace Moq with NSubstitute across all test projects.
Implementation
- Replace
Moq package with NSubstitute in Directory.Packages.props
- Rewrite mock setups — API comparison:
| Moq |
NSubstitute |
var mock = new Mock<IFoo>(); |
var sub = Substitute.For<IFoo>(); |
mock.Setup(x => x.Method()).Returns(val) |
sub.Method().Returns(val) |
mock.Object |
sub (used directly) |
mock.Verify(x => x.Method(), Times.Once) |
sub.Received(1).Method() |
TechHubApiClient can be sealed once Moq is removed (tests mock the interface, not the class)
Notes
- Large refactor — consider doing incrementally: new tests use NSubstitute, migrate old tests over time
- Low urgency; only do when there's a natural opportunity
Priority
Low (non-urgent)
Context
All test projects currently use Moq for mocking.
TechHubApiClientis intentionally non-sealed to support Moq's proxy generation. Moq had the SponsorLink controversy and requires non-sealed classes.NSubstitute has a cleaner API and works directly with interfaces (no need for non-sealed classes).
Target State
Replace Moq with NSubstitute across all test projects.
Implementation
Moqpackage withNSubstituteinDirectory.Packages.propsvar mock = new Mock<IFoo>();var sub = Substitute.For<IFoo>();mock.Setup(x => x.Method()).Returns(val)sub.Method().Returns(val)mock.Objectsub(used directly)mock.Verify(x => x.Method(), Times.Once)sub.Received(1).Method()TechHubApiClientcan be sealed once Moq is removed (tests mock the interface, not the class)Notes
Priority
Low (non-urgent)