Some features ship because they're urgent. Others ship because you finally got the design right. The v3 release of DepenMock is firmly in the second camp.
When I first started building DepenMock, I had two long-term goals in mind: abstract the testing framework (so you could use NUnit, MSTest, or xUnit) and abstract the mocking framework (so you weren't locked into Moq). The testing framework side came together early, that's been in place since the beginning. The mocking framework side took three attempts and a couple of hard lessons before it landed correctly.
With v3.0 and v3.1, that second goal is done. DepenMock now supports Moq, NSubstitute, and FakeItEasy; and you can swap between them with a single line of code.
A Quick Recap (New to DepenMock?)
DepenMock is a C# testing library that eliminates the boilerplate of setting up mocked dependencies in unit tests. Instead of manually constructing every mock and wiring them into your system under test, you inherit from a base class and get all of that for free.
public class OrderServiceTests : BaseTestByAbstraction<OrderService, IOrderService>
{
public OrderServiceTests() : base(new MoqMockFactory()) { }
[Fact]
public void ProcessOrder_ValidRequest_SavesOrder()
{
// Arrange
var order = Container.Create<Order>();
Container.ResolveMock<IOrderRepository>().AsMoq()
.Setup(x => x.Save(It.IsAny<Order>()));
var sut = ResolveSut();
// Act
sut.ProcessOrder(order);
// Assert
Container.ResolveMock<IOrderRepository>().AsMoq()
.Verify(x => x.Save(order), Times.Once);
}
}If this is the first time you're hearing about DepenMock, check out the intro post for a fuller walkthrough. The rest of this post assumes you're familiar with the basics.
How We Got Here: Three Attempts at the Right Abstraction
Attempt One: Scrapped
The first attempt at abstracting the mocking framework never shipped. The design felt off early, and rather than push it through, I scrapped it. No concrete artifacts to show here, just the lesson that the right abstraction wasn't obvious.
Attempt Two: v2.0.0 - The Foundation
v2.0.0 introduced the IMock<T> and IMockFactory interfaces, which gave DepenMock a mocking-framework-agnostic core for the first time. Moq moved out of the core package and into its own DepenMock.Moq package. ResolveMock<T>() started returning IMock<T> instead of Moq's Mock<T> directly.
This was real progress, but when I tried to wire up a second mocking framework on top of the v2 design, it became clear the abstraction still wasn't quite right. The seams were in the wrong places.
Attempt Three: v3.0.0 - Getting It Right
v3 redesigned how the mock factory is threaded through the base classes. Instead of the base classes managing factory creation internally, the factory is now passed explicitly to the constructor. That one shift made everything click, and suddenly adding NSubstitute (v3.0.0) and FakeItEasy (v3.1.0) was straightforward.
What's New in v3.0: NSubstitute Support
v3.0.0 ships a new DepenMock.NSubstitute package. If your team prefers NSubstitute's argument-style API, you can now use it with DepenMock without any other changes to how your tests are structured.
public class OrderServiceTests : BaseTestByAbstraction<OrderService, IOrderService>
{
public OrderServiceTests() : base(new NSubstituteMockFactory()) { }
[Fact]
public void ProcessOrder_ValidRequest_SavesOrder()
{
// Arrange
var order = Container.Create<Order>();
var sut = ResolveSut();
// Act
sut.ProcessOrder(order);
// Assert
Container.ResolveMock<IOrderRepository>().AsNSubstitute()
.Received(1).Save(order);
}
}The .AsNSubstitute() extension method unwraps the IMock<T> into NSubstitute's native substitute so you can use its full API; Received(), Returns(), Arg.Any<T>(), and everything else you're used to.
v3.0 Breaking Change: Explicit Mock Factory
The base classes no longer create a MoqMockFactory internally. You now pass the factory yourself. This is a one-line change, but it is a compile-time break โ see the migration guide below.
What's New in v3.1: FakeItEasy Support
v3.1.0 follows the same pattern with a new DepenMock.FakeItEasy package. If you're a FakeItEasy shop, here's what using it looks like:
public class OrderServiceTests : BaseTestByAbstraction<OrderService, IOrderService>
{
public OrderServiceTests() : base(new FakeItEasyMockFactory()) { }
[Fact]
public void ProcessOrder_ValidRequest_SavesOrder()
{
// Arrange
var order = Container.Create<Order>();
var fakeRepo = Container.ResolveMock<IOrderRepository>().AsFake();
var sut = ResolveSut();
// Act
sut.ProcessOrder(order);
// Assert
A.CallTo(() => fakeRepo.Save(order)).MustHaveHappenedOnceExactly();
}
}.AsFake() unwraps the IMock<T> into FakeItEasy's native fake, giving you access to A.CallTo(), MustHaveHappened*(), and the rest of FakeItEasy's API.
The Full Framework Matrix
Here's where DepenMock stands today across both axes, testing framework and mocking framework:
| Package | Testing Framework | Mocking Framework |
|---|---|---|
DepenMock.XUnit + DepenMock.Moq |
xUnit | Moq |
DepenMock.XUnit + DepenMock.NSubstitute |
xUnit | NSubstitute |
DepenMock.XUnit + DepenMock.FakeItEasy |
xUnit | FakeItEasy |
DepenMock.NUnit + DepenMock.Moq |
NUnit | Moq |
DepenMock.NUnit + DepenMock.NSubstitute |
NUnit | NSubstitute |
DepenMock.NUnit + DepenMock.FakeItEasy |
NUnit | FakeItEasy |
DepenMock.MSTest + DepenMock.Moq |
MSTest | Moq |
DepenMock.MSTest + DepenMock.NSubstitute |
MSTest | NSubstitute |
DepenMock.MSTest + DepenMock.FakeItEasy |
MSTest | FakeItEasy |
Mix and match however your team prefers.
Migrating to v3
If you're upgrading from v2, here's what you need to change.
1. Add your mocking framework package
If you're sticking with Moq, make sure you have a reference to DepenMock.Moq. For NSubstitute or FakeItEasy, add the corresponding package:
dotnet add package DepenMock.Moq
dotnet add package DepenMock.NSubstitute
dotnet add package DepenMock.FakeItEasy2. Add a constructor to your test classes
Every test class that inherits from a DepenMock base class now needs to pass a mock factory to the base constructor. Pick the factory that matches your mocking framework:
// Before (v2.0.0) - no constructor needed
public class OrderServiceTests : BaseTestByAbstraction<OrderService, IOrderService>
{
// tests...
}
// After (v3.0.0) - pass the factory explicitly
public class OrderServiceTests : BaseTestByAbstraction<OrderService, IOrderService>
{
public OrderServiceTests() : base(new MoqMockFactory()) { }
// tests...
}The same applies to BaseTestByType<T> and any class that extends BaseTest directly. If you're on a large codebase, this is a global find-and-replace job โ but it only touches the constructor, nothing inside the tests themselves.
3. Update ResolveMock<T>() call sites (if upgrading from v1)
If you skipped v2 and are jumping straight from v1 to v3, you'll also need to add .AsMoq() wherever you call Moq APIs on a resolved mock:
// v1 style
Container.ResolveMock<IOrderRepository>()
.Setup(x => x.Save(It.IsAny<Order>()));
// v3 style
Container.ResolveMock<IOrderRepository>().AsMoq()
.Setup(x => x.Save(It.IsAny<Order>()));Installing
Pick the combination that matches your stack:
# Core + your test framework
dotnet add package DepenMock
dotnet add package DepenMock.XUnit # or .NUnit / .MSTest / .XUnit.V3
# Your mocking framework
dotnet add package DepenMock.Moq # or .NSubstitute / .FakeItEasyWrapping Up
v3 isn't the flashiest release, but it's one of the most satisfying โ it closes out a goal that's been on the roadmap since the beginning. DepenMock now works the way I always intended it to: fully agnostic about both your testing framework and your mocking framework.
If you've been holding off on adopting DepenMock because you're a NSubstitute or FakeItEasy team, now's a good time to give it a try.
๐ GitHub repo ยท NuGet packages
Happy testing!

