API Replacements And Migration Exceptions
This page collects the high-churn API replacement rules, compatibility-only exceptions, and worked migration examples that used to live in the main migration guide.
Open it when you know the specific API or helper you are replacing and need the detailed guidance rather than the front-door migration flow.
Real-world gotchas
These are small migration edges that cause disproportionate churn in provider-first rewrites.
| Legacy habit | Preferred v4 move | Why it trips people up |
|---|---|---|
mock.Object or GetMock<T>().Object |
fastMock.Instance, Mocks.GetObject<T>(), or Mocks.GetRequiredObject<T>() |
GetOrCreateMock<T>() returns IFastMock<T>, not a raw Mock<T> |
mock.Reset() |
fastMock.Reset() |
The provider-first handle already has a reset surface; AsMoq() is only needed when you truly need raw Moq APIs |
TimesSpec.Never during bulk conversion |
TimesSpec.Never() or preferably TimesSpec.NeverCalled |
The method call is easy to miss, and NeverCalled avoids the missing-parentheses mistake entirely |
| one unkeyed mock for two keyed constructor dependencies | keyed MockRequestOptions.ServiceKey, AddKeyedType(...), or explicit constructor injection |
a single type-based double cannot catch public or private or primary or secondary swaps |
shared helper signatures such as Func<Times> |
deliberate helper pass to TimesSpec |
Fixing the helper boundary first is cheaper than patching repeated leaf call sites |
Equality intent in migrated matcher expressions
Make equality intent explicit while you migrate matcher predicates.
var expectedCommand = new OrderCommand("cust-42", 149.90m);
mock.Setup(x => x.Send(It.Is<OrderCommand>(command => command == expectedCommand)));
var expectedSession = new UploadSession();
mock.Setup(x => x.Attach(It.Is<UploadSession>(session => ReferenceEquals(session, expectedSession))));
mock.Setup(x => x.Publish(It.Is<OutboundMessage>(message => message.Id == expectedId)));
Use those three patterns deliberately:
- value equality for records or value objects when
==orEquals(...)is intentionally part of the type contract - identity with
ReferenceEquals(...)when the same mutable instance matters - property-based intent when neither pure value equality nor identity is the real requirement
Reflection-provider caveat:
- reflection verification only supports direct method-call expressions and only compares direct constant arguments with
Equals(...) - captured variables and richer provider-native matcher semantics are not interpreted there, so do not present reflection verification as equivalent to Moq or NSubstitute matcher behavior
If you still need raw Moq APIs after moving to GetOrCreateMock<T>(), step through AsMoq() explicitly. Otherwise stay on Instance, Reset(), GetObject<T>(), and the provider-neutral verification surface.
Shared helper signature example:
// Old helper boundary
void AssertLogged(Mock<ILogger> logger, Func<Times> times)
// Deliberate migration boundary
void AssertLogged(Mocker mocker, TimesSpec times)
.Object on tracked FastMoq mocks
Old pattern:
var logger = Mocks.GetMock<ILogger<OrderService>>().Object;
Current guidance when the code only needs the dependency instance:
var logger = Mocks.GetObject<ILogger<OrderService>>();
Or, when the code already has a tracked handle:
var loggerMock = Mocks.GetOrCreateMock<ILogger<OrderService>>();
var logger = loggerMock.Instance;
If the old call site used GetRequiredTrackedMock<T>() and only needs the instance, use GetRequiredObject<T>() instead.
Why:
- use
.Instancewhen you already have anIFastMock<T>handle - use
GetObject<T>()orGetRequiredObject<T>()when the code is retrieving the dependency fromMockeronly to get the instance - keep
AsMoq().Objectonly for a deliberate local raw-Moq compatibility pocket
Verification-only Mock<T> aliases
Old pattern:
private Mocker Mocks { get; } = new();
private Mock<IOrderGateway> GatewayMock => Mocks.GetOrCreateMock<IOrderGateway>().AsMoq();
void AssertSent()
{
GatewayMock.Verify(x => x.Send("alpha"));
}
Current guidance:
private Mocker Mocks { get; } = new();
void AssertSent()
{
Mocks.Verify<IOrderGateway>(x => x.Send("alpha"), TimesSpec.Once);
}
Or, when you still want a reusable tracked handle for arrangement:
private Mocker Mocks { get; } = new();
private IFastMock<IOrderGateway> Gateway => Mocks.GetOrCreateMock<IOrderGateway>();
Why:
- a
Mock<T>alias that only exists for laterVerify(...)calls keeps the test on the Moq surface without adding behavior - keep verification explicit with
Mocker.Verify<T>(...) - keep a
Mock<T>alias only when the symbol also needs Moq-specific setup or other rawMock<T>behavior such asProtected()orSetupSet(...)
Old to new guidance
Initialize<T>(...)
Old pattern:
Mocks.Initialize<IOrderRepository>(mock =>
mock.Setup(x => x.Load(123)).Returns(order));
Current guidance:
Mocks.GetOrCreateMock<IOrderRepository>()
.Setup(x => x.Load(123))
.Returns(order);
Why:
Initialize<T>(...)is now a compatibility wrapper, not the recommended setup entry point.GetOrCreateMock<T>()plus the Moq provider extensions is the preferred v4 transition path for Moq-specific setup.GetMock<T>()remains only as an obsolete compatibility surface when the assembly is explicitly using the bundled Moq compatibility provider.
If you are already refactoring the test and do not specifically need a raw Moq.Mock<T>, prefer moving toward provider-neutral retrieval and verification instead of expanding direct GetMock<T>() usage further.
There is not yet a single provider-neutral drop-in replacement for Initialize<T>(...), because Initialize<T>(...) wraps a Moq Setup(...) callback.
In the v4 transition, the preferred provider-package shortcut for Moq-specific setup is now:
Mocks.GetOrCreateMock<IOrderRepository>()
.Setup(x => x.Load(123))
.Returns(order);
That shortcut comes from FastMoq.Provider.Moq and forwards to AsMoq() internally. It keeps the core abstractions provider agnostic while giving Moq-based test suites a smoother migration path.
Required namespace for those fluent Moq-backed shortcuts:
using FastMoq.Providers.MoqProvider;
Without that namespace, Setup(...), SetupGet(...), SetupSequence(...), and Protected() on IFastMock<T> can appear to have "disappeared" even though the provider package is installed.
Practical rule:
- use
GetOrCreateMock<T>()plus the Moq provider extensions when you want tracked provider-first access with Moq-specific setup convenience in v4 - keep existing
GetMock<T>()usage only when the test still depends on older directMock<T>-shaped compatibility code and you are minimizing churn during the v4 transition - use
GetOrCreateMock(...),Verify(...), andVerifyLogged(...)when the test can move forward without Moq-specific setup semantics - use
AddType(...)when the cleaner provider-neutral move is to supply a fake, stub, factory, or fixed instance rather than configure a mocking-library setup chain
Provider-neutral replacement depends on what the old Initialize<T>(...) callback was doing:
- if the old code mainly existed to verify later calls, start from
GetOrCreateMock<T>()and provider-neutral verification - if the old code mainly existed to supply behavior, prefer
AddType(...)with a fake, stub, or fixed instance when practical - if the old code still needs Moq
Setup(...)semantics, prefer moving it toGetOrCreateMock<T>()with the Moq provider extensions and keepGetMock<T>()only when preserving the old Moq shape is materially cheaper than rewriting the test
SetupHttpMessage(...) and HTTP helpers
Old Moq-oriented pattern:
Mocks.SetupHttpMessage(HttpStatusCode.OK, "{\"id\":42}");
Current guidance for new or refactored tests:
Mocks.WhenHttpRequestJson(HttpMethod.Get, "/orders/42", "{\"id\":42}");
Or, when the response needs full control:
Mocks.WhenHttpRequest(HttpMethod.Get, "/orders/42", () =>
new HttpResponseMessage(HttpStatusCode.OK));
Why:
- the provider-neutral HTTP behavior helpers now live in core and are the preferred long-term path
- the older Moq-shaped HTTP setup helpers are compatibility APIs for suites that still depend on Moq-specific behavior such as protected
SendAsyncsetups - the migration concern is package ownership, not namespace churn
Compatibility detail:
- keep
using FastMoq.Extensions; - keep or add the
FastMoq.Provider.Moqpackage when you still needSetupHttpMessage(...) - prefer
WhenHttpRequest(...)andWhenHttpRequestJson(...)for newly written tests and while refactoring existing ones
That means existing source often does not need a namespace rewrite for the older HTTP helpers. The moved compatibility methods intentionally remain in FastMoq.Extensions; they are simply provided by the Moq package now instead of core.
VerifyLogger(...) vs VerifyLogged(...)
Old Moq-oriented pattern:
Mocks.GetMock<ILogger<MyComponent>>()
.VerifyLogger(LogLevel.Information, "Processing started");
Current guidance:
Mocks.VerifyLogged(LogLevel.Information, "Processing started");
Why:
VerifyLogged(...)is the provider-safe logging assertion surface.VerifyLogger(...)remains a Moq compatibility API.- existing logger assertions carried forward from previous FastMoq versions can stay on
VerifyLogger(...)if the assembly explicitly selectsmoq, but new or touched tests should preferVerifyLogged(...).
Analyzer note:
FMOQ0003points shared helper cleanup and leaf test rewrites towardVerifyLogged(...).- if the same helper also registered loggers manually, prefer
AddLoggerFactory()orCreateLoggerFactory()from the finalized helper surface instead of preserving a private logger wrapper.
ILogger.Log<TState> callback setup vs SetupLoggerCallback(...)
Old Moq-oriented pattern:
Mocks.GetOrCreateMock<ILogger>()
.Setup(x => x.Log(
It.IsAny<LogLevel>(),
It.IsAny<EventId>(),
It.Is<It.IsAnyType>((_, _) => true),
It.IsAny<Exception>(),
It.IsAny<Func<It.IsAnyType, Exception?, string>>()))
.Callback(new InvocationAction(invocation =>
{
var message = invocation.Arguments[2]?.ToString() ?? string.Empty;
output.WriteLine(message);
}));
Current guidance:
Mocks.SetupLoggerCallback((_, _, message, _) =>
{
output.WriteLine(message);
});
Why:
SetupLoggerCallback(...)keeps the test on FastMoq's provider-neutral logger capture path.FastArgdoes not replace theIt.IsAnyTypeportion here because that placeholder stands in for the genericTStateclosed byILogger.Log<TState>, not just a normal argument matcher.- keep the Moq compatibility path only when the callback intentionally inspects raw structured logger state rather than normalized message and exception output.
Analyzer note:
FMOQ0036points safe trackedILogger.Log<TState>setup callbacks towardSetupLoggerCallback(...).
IOptions<T> setup and real instances
Old patterns often looked like this:
Mocks.GetMock<IOptions<MyOptions>>()
.Setup(x => x.Value)
.Returns(new MyOptions { RetryCount = 3 });
or:
Mocks.AddType<IOptions<MyOptions>>(Options.Create(new MyOptions { RetryCount = 3 }), true);
Current guidance:
Mocks.SetupOptions(new MyOptions { RetryCount = 3 }, replace: true);
Or, when the test only needs a default instance:
Mocks.SetupOptions<MyOptions>();
Why:
SetupOptions(...)keeps the commonIOptions<T>registration shape explicit without preserving Moq-only property setup boilerplate.- It is provider-neutral and matches the migration intent directly: register a concrete options value for
IOptions<T>. - It keeps
AddType(...)focused on non-options real instances such asMemoryCache, file systems, clocks, or fixed collaborators.
Practical rule:
- use
SetupOptions(...)when the dependency under test isIOptions<T> - use
AddType(instance)when the dependency itself is a concrete runtime object or interface implementation rather than an options wrapper - keep
GetMock<IOptions<T>>()only when the test genuinely depends on mocking-library-specific behavior beyond returning a concreteValue
Analyzer note:
FMOQ0019points the commonGetMock<IOptions<T>>().Setup(x => x.Value)...andAddType<IOptions<T>>(Options.Create(...))shapes towardSetupOptions(...).
TimesSpec shape in v4
Use TimesSpec with this mental model during migration:
- property-style aliases for the common cases:
TimesSpec.OnceandTimesSpec.NeverCalled - method calls when a count is required:
TimesSpec.Exactly(count),TimesSpec.AtLeast(count), andTimesSpec.AtMost(count) TimesSpec.Never()still works as a compatibility form, but preferTimesSpec.NeverCalledin new migration edits when you want the shape to read consistently withTimesSpec.Once
Examples:
Mocks.Verify<IOrderRepository>(x => x.Save(order), TimesSpec.Once);
Mocks.Verify<IEmailGateway>(x => x.Send("finance@contoso.test"), TimesSpec.NeverCalled);
Mocks.VerifyLogged(LogLevel.Warning, "retrying", TimesSpec.Exactly(2));
This is one of the easiest compile-fix churn points in migration work, so it is worth being explicit.
EnsureNullCheckThrown(...) output callbacks
Old framework-coupled pattern:
action.EnsureNullCheckThrown(parameterName, constructorName, output);
Current guidance:
action.EnsureNullCheckThrown(parameterName, constructorName, output.WriteLine);
Or, when the test does not need diagnostic output at all:
action.EnsureNullCheckThrown(parameterName, constructorName);
Why:
- FastMoq no longer carries a test-framework-specific output-helper overload for this path.
- The supported replacement is the framework-neutral
Action<string>callback overload. - Test-framework adapters should stay in the test project or local helper layer rather than in the shared FastMoq helper surface.
Practical migration rule:
- if the test already has an xUnit
ITestOutputHelper, passoutput.WriteLine - if the test uses another framework, pass that framework's equivalent line-writer delegate
- if the output was only diagnostic noise, drop the callback entirely and keep the constructor assertion helper
Strict
Old assumption:
Mocks.Strict = true;
often implied "switch the whole runtime into a strict profile."
Current guidance:
Mocks.Behavior.Enabled |= MockFeatures.FailOnUnconfigured;
or, if you want the full preset:
Mocks.UseStrictPreset();
Why:
Strictis now treated as a compatibility alias forMockFeatures.FailOnUnconfigured.- Full-profile behavior changes belong to
Behavioror the preset helpers. - If older tests meant "use the full strict profile," they should move to
UseStrictPreset()explicitly. - New repo-era code should treat
FailOnUnconfigurednarrowly: it controls mock strictness, not every older strict-era fallback and built-in resolution rule.
Breaking-change note:
Strictno longer implies the entire old strict profile by itselfUseStrictPreset()is now the clearer replacement when the broader strict behavior profile is intendedStrictstill exists to stamp compatibility defaults onto the current policy surface, so this is a narrowing rather than a full removal of old semantics- strict
IFileSystemno longer guarantees a raw or empty mock in the current v4 release line - tracked
IFileSystemmocks may still expose built-in members such asFile,Directory, andPath - if a test carried forward from previous FastMoq versions relied on null members, configure them explicitly on
GetMock<IFileSystem>() - this compatibility note is specific to
IFileSystem; it is not a blanket statement that all built-in types ignore strict-mode behavior
Example:
Mocks.UseStrictPreset();
Mocks.Behavior.Enabled |= MockFeatures.FailOnUnconfigured;
Mocks.GetMock<IFileSystem>()
.Setup(x => x.Directory)
.Returns((IDirectory)null!);
For the dedicated compatibility summary, see Breaking Changes.
For the fuller current-model explanation of Strict, FailOnUnconfigured, and the preset helpers, see Testing Guide: Strict vs Presets.
Legacy GetMock<T>() vs AddType(...)
Old usage sometimes blurred these together.
Current guidance:
- prefer
GetOrCreateMock<T>()when the dependency is still conceptually a tracked mock - use
AddType(...)when you are overriding resolution with a concrete type, custom factory, or fixed instance - keep
GetMock<T>()only when you are deliberately staying on the obsolete Moq compatibility path during v4 migration
Good AddType(...) use:
Mocks.AddType<IClock>(_ => new FakeClock(DateTimeOffset.Parse("2026-04-01T12:00:00Z")));
Good provider-first mock use:
Mocks.GetOrCreateMock<IEmailGateway>()
.Setup(x => x.SendAsync(It.IsAny<string>()))
.Returns(Task.CompletedTask);
GetRequiredMock<T>()
GetRequiredMock<T>() is the next legacy retrieval surface after GetMock<T>(), but it does not have a single safe one-line replacement.
Use this decision table instead of a blind search-and-replace:
| If the old intent was | Prefer this | Why |
|---|---|---|
| create-or-reuse a tracked mock handle | GetOrCreateMock<T>() |
the test did not really need the "must already exist" contract |
| require that the dependency was already tracked before this line | GetRequiredTrackedMock<T>() or TryGetTrackedMock<T>(...) |
this keeps the old precondition without falling back to raw Mock<T> |
only get the dependency instance, not the raw Mock<T> |
GetObject<T>() or GetRequiredObject<T>() |
the test is consuming the resolved instance, not the mocking-library wrapper |
| keep using raw Moq for a local compatibility pocket | GetRequiredTrackedMock<T>().AsMoq() or GetOrCreateMock<T>().AsMoq() depending on whether existence is required |
the test still depends on raw Mock<T> APIs |
Before:
var logger = Mocks.GetRequiredMock<ILogger<OrderService>>();
logger.VerifyLogger(LogLevel.Information, "Processing started");
After, when creation is acceptable and the test only needs the tracked handle:
var logger = Mocks.GetOrCreateMock<ILogger<OrderService>>();
logger.VerifyLogger(LogLevel.Information, "Processing started");
After, when the old call really meant "this mock must already be tracked":
var logger = Mocks.GetRequiredTrackedMock<ILogger<OrderService>>();
After, when the test wants to branch instead of throwing:
if (!Mocks.TryGetTrackedMock<ILogger<OrderService>>(out var logger))
{
return;
}
Required object retrieval
After, when the old code only needed the resolved dependency instance:
var logger = Mocks.GetRequiredObject<ILogger<OrderService>>();
Why FMOQ0017 is warning-only:
GetRequiredMock<T>()mixes two concerns: legacy raw-Moq access and an existence precondition- different call sites want different replacements
- an automatic
GetOrCreateMock<T>()rewrite could silently weaken the test by creating something that used to be required to exist already
Legacy mock creation and lifecycle helpers
These APIs are still supported in v4 as compatibility surfaces, but they are no longer the preferred direction for touched tests.
| Legacy API | Preferred move | Typical manual rewrite |
|---|---|---|
CreateMock<T>(...) |
tracked provider-first mock | GetOrCreateMock<T>(...) or CreateFastMock<T>() when you specifically want immediate tracked registration |
CreateMockInstance<T>(...) |
tracked instance or detached provider-first handle | a fresh Mocker and GetOrCreateMock<T>(), then Instance, or MockingProviderRegistry.Default.CreateMock<T>().Instance when tracking is not needed |
CreateDetachedMock<T>(...) |
standalone provider-first mock | CreateStandaloneFastMock<T>() or MockingProviderRegistry.Default.CreateMock<T>() |
AddMock<T>(...) |
tracked provider-first mock or explicit concrete override | CreateFastMock<T>(), GetOrCreateMock<T>(), or AddType<T>(...) depending on whether the registration must be new, reusable, or concrete |
RemoveMock(...) |
new scope instead of mutating tracked legacy mocks | create a fresh Mocker for the branch that needs a different setup |
Important distinction:
CreateFastMock<T>()registers the new mock in the currentMockerCreateStandaloneFastMock<T>()does not register itCreateFastMock<T>()is therefore not a safe replacement forCreateDetachedMock<T>()when the same unkeyed type is already tracked
Current tracked-versus-detached decision table:
| If the migrated double should... | Use... | Verify with... |
|---|---|---|
stay as the normal tracked dependency in the current Mocker |
GetOrCreateMock<T>() |
Mocks.Verify<T>(...) |
| become a new tracked registration immediately | CreateFastMock<T>() |
Mocks.Verify<T>(...) |
stay detached but still come from the current Mocker |
CreateStandaloneFastMock<T>() |
MockingProviderRegistry.Default.Verify(...) |
stay detached with no parent Mocker registration at all |
MockingProviderRegistry.Default.CreateMock<T>() |
MockingProviderRegistry.Default.Verify(...) |
If GetOrCreateMock<T>() fails with AmbiguousImplementationException, do not loop back to another unkeyed tracked mock. Move to AddType(...) when the test means one specific implementation, or move to one of the detached paths when the collaborator truly lives outside the parent tracked graph.
Before, detached legacy mock creation:
var emailGateway = Mocks.CreateDetachedMock<IEmailGateway>();
emailGateway.Setup(x => x.SendAsync("finance@contoso.test")).Returns(Task.CompletedTask);
var service = new CheckoutService(emailGateway.Object);
After, provider-first standalone mock:
var emailGateway = Mocks.CreateStandaloneFastMock<IEmailGateway>();
emailGateway.Setup(x => x.SendAsync("finance@contoso.test")).Returns(Task.CompletedTask);
var service = new CheckoutService(emailGateway.Instance);
Detached provider-neutral verification keeps the assert side out of AsMoq().Verify(...), even when the member returns Task or Task<T>:
await service.NotifyFinanceAsync(CancellationToken.None);
MockingProviderRegistry.Default.Verify(
emailGateway,
x => x.SendAsync("finance@contoso.test"),
TimesSpec.Once);
MockingProviderRegistry.Default.VerifyNoOtherCalls(emailGateway);
The verification expression still uses Expression<Action<T>>; the async-returning call is valid there because the return value is intentionally discarded in the assertion expression.
Before, adding a legacy Moq mock into the tracked collection:
var clock = new Mock<IClock>();
Mocks.AddMock(clock);
After, when the dependency should stay a tracked mock:
var clock = Mocks.CreateFastMock<IClock>();
After, when the dependency is really a fixed fake or stub:
Mocks.AddType<IClock>(_ => new FakeClock(DateTimeOffset.Parse("2026-04-01T12:00:00Z")));
Before, removing and swapping a legacy mock:
Mocks.RemoveMock(existingCacheMock);
Mocks.AddMock(new Mock<ICache>());
After, prefer a fresh scope for the alternate setup:
var isolatedMocks = new Mocker();
var cache = isolatedMocks.GetOrCreateMock<ICache>();
Why FMOQ0018 is warning-only:
- these helpers do not all collapse to the same target API
- some old call sites want tracked mocks, some want resolved instances, and some really want separate test scopes
- an auto-fix here would be more likely to hide a semantic change than to save useful migration work
DbContext package boundary
In 3.0.0, core directly carried the EF packages and the Moq-based DbContextMock<TContext> implementation.
Current v4 package direction:
GetMockDbContext<TContext>()still lives in theFastMoqnamespace for the main call site.FastMoq.Coreis being kept lighter and no longer owns the EF package references.FastMoq.Databasenow owns the EF-specific helper implementation.
Practical guidance:
- if you install
FastMoq, keep usingGetMockDbContext<TContext>()as before - if you install
FastMoq.Coredirectly, also installFastMoq.Database - use
GetDbContextHandle<TContext>(...)when you need to choose explicitly between mocked sets and a real in-memory EF context - once
GetMockDbContext<TContext>()orGetDbContextHandle<TContext>(...)has tracked the DbContext in mocked-sets mode,GetOrCreateMock<TContext>()andGetMockModel<TContext>()return that same tracked context through the provider-first APIs
The current v4 package line makes the mode split explicit:
var handle = mocker.GetDbContextHandle<ApplicationDbContext>(new DbContextHandleOptions<ApplicationDbContext>
{
Mode = DbContextTestMode.RealInMemory,
});
var dbContext = handle.Context;
AddKnownType(...) vs AddType(...)
This distinction matters more in the current v4 release line than it did in previous FastMoq versions.
Use AddType(...) when you are overriding normal resolution for a dependency:
Mocks.AddType<IClock>(_ => new FakeClock(DateTimeOffset.Parse("2026-04-01T12:00:00Z")));
Use AddKnownType(...) when you are extending FastMoq's built-in handling for a framework-style type:
Mocks.AddKnownType<IFileSystem>(
directInstanceFactory: (_, _) => new MockFileSystem().FileSystem,
includeDerivedTypes: true);
The generic public API is now more strongly typed for value callbacks, so object-default hooks can work directly with TKnown:
Mocks.AddKnownType<IHttpContextAccessor>(
applyObjectDefaults: (_, accessor) =>
{
accessor.HttpContext!.TraceIdentifier = "integration-test";
},
includeDerivedTypes: true);
Practical rule:
AddType(...)answers "resolve this abstraction differently"AddKnownType(...)answers "handle this kind of framework-heavy type differently inside FastMoq's known-type pipeline"
For most ordinary application dependencies, prefer AddType(...).
For framework helpers like IFileSystem, HttpClient, DbContext, and HttpContext, prefer AddKnownType(...).
Construction APIs
The older entry points still exist:
CreateInstance<T>(...)CreateInstanceByType<T>(...)
Current guidance for new code:
var component = Mocks.CreateInstanceByType<MyComponent>(
InstanceCreationFlags.AllowNonPublicConstructorFallback,
typeof(int),
typeof(string));
Why:
- the implementation now uses a single flags-based override model
- the call site communicates whether constructor fallback is using policy, public-only, or explicitly allowed
If you want to decouple constructor fallback from global policy, use an explicit flag:
var component = Mocks.CreateInstance<MyComponent>(InstanceCreationFlags.AllowNonPublicConstructorFallback);
The default constructor-fallback policy is now also explicit at the Mocker level:
Mocks.Policy.DefaultFallbackToNonPublicConstructors = false;
Public Test Classes And Internal Components
FastMoq now supports non-public component construction through the same flags-based model, but it does not change C# accessibility rules.
If a public test class needs to validate an internal or protected component, keep the public test as a wrapper and move the MockerTestBase<TComponent> inheritance into an internal helper:
public class InternalOrderRulesTests
{
[Fact]
public void PublicTestClass_CanExercise_InternalService()
{
using var harness = new InternalOrderRulesHarness();
harness.Sut.IsPriority("P1").Should().BeTrue();
}
}
internal sealed class InternalOrderRulesHarness : MockerTestBase<InternalOrderRules>
{
protected override Action<MockerPolicyOptions>? ConfigureMockerPolicy => policy =>
{
policy.DefaultFallbackToNonPublicConstructors = false;
};
protected override InstanceCreationFlags ComponentCreationFlags
=> InstanceCreationFlags.AllowNonPublicConstructorFallback;
internal InternalOrderRules Sut => Component;
}
Use InternalsVisibleTo when the SUT lives in another assembly.
The supported runtime behavior is: FastMoq can create the non-public component once the test can legally reference it.
The unsupported assumption is: FastMoq can make public class MyTests : MockerTestBase<InternalType> compile. That remains a compiler restriction, not a FastMoq runtime limitation.
Explicit Constructor Selection In Tests
When tests need a specific constructor, prefer selecting it from the test base instead of annotating production code for test-only reasons.
For MockerTestBase<TComponent>, the primary hook is ComponentConstructorParameterTypes:
internal sealed class OrderRulesTestBase : MockerTestBase<OrderRules>
{
protected override Type?[]? ComponentConstructorParameterTypes
=> new Type?[] { typeof(IFileSystem), typeof(string) };
}
If constructor selection also needs custom creation logic, use CreateComponentAction:
internal sealed class OrderRulesTestBase : MockerTestBase<OrderRules>
{
protected override Func<Mocker, OrderRules> CreateComponentAction => mocker =>
mocker.CreateInstanceByType<OrderRules>(
InstanceCreationFlags.AllowNonPublicConstructorFallback,
typeof(IFileSystem),
typeof(string))!;
}
The older MockerTestBase(params Type[] createArgumentTypes) constructor remains available for compatibility, but the property-based hook is the recommended path for new test bases.
Constructor Ambiguity Overrides
FastMoq keeps the existing ambiguity-throw behavior by default.
That means a suite that already expected an AmbiguousImplementationException continues to behave the same way until you opt into a different fallback rule.
Current additive options are:
Mocks.Policy.DefaultConstructorAmbiguityBehavior = ConstructorAmbiguityBehavior.PreferParameterlessConstructor;
var component = Mocks.CreateInstance<MyComponent>(
InstanceCreationFlags.PreferParameterlessConstructorOnAmbiguity);
If production code itself should advertise a preferred default constructor for all callers, mark it explicitly:
internal sealed class OrderRules
{
[PreferredConstructor]
public OrderRules()
{
}
public OrderRules(IFileSystem fileSystem)
{
}
}
Constructor-selection diagnostics for these paths are captured in Mocks.LogEntries.
Obsolete MockOptional
Old pattern:
Mocks.MockOptional = true;
Current guidance for constructor creation:
Mocks.OptionalParameterResolution = OptionalParameterResolutionMode.ResolveViaMocker;
var component = Mocks.CreateInstance<MyComponent>();
Current guidance for method or delegate invocation:
var result = Mocks.CallMethod<MyResult>(new InvocationOptions
{
OptionalParameterResolution = OptionalParameterResolutionMode.ResolveViaMocker,
}, (Func<IMyDependency?, MyResult>)CreateResult);
If reflected method fallback should be controlled independently of Strict, set it explicitly:
var result = Mocks.InvokeMethod(new InvocationOptions
{
FallbackToNonPublicMethods = false,
}, target, "Run");
The default reflected-method fallback policy is also configurable on the Mocker instance:
Mocks.Policy.DefaultFallbackToNonPublicMethods = false;
Built-in framework helpers are now separately controllable through flags:
Mocks.Policy.EnabledBuiltInTypeResolutions =
BuiltInTypeResolutionFlags.FileSystem |
BuiltInTypeResolutionFlags.DbContext;
That policy controls whether built-in direct or managed resolution is available for:
IFileSystemHttpClientUriDbContext
This keeps known-type behavior separate from FailOnUnconfigured so the option means what it says.
Provider-backed mock creation also has a Mocker-level default policy:
Mocks.Policy.DefaultStrictMockCreation = true;
Use that when you want new provider-backed mocks to be created as strict without relying on the broader compatibility bundle.
GetMockDbContext<TContext>() remains a deliberate exception and stays on the supported DbContext helper behavior rather than adopting strict creation by default.
For MockerTestBase<TComponent>, the same defaults can be applied before component creation through the policy hook:
protected override Action<MockerPolicyOptions>? ConfigureMockerPolicy => policy =>
{
policy.DefaultStrictMockCreation = true;
};
Current guidance for MockerTestBase<TComponent>:
protected override InstanceCreationFlags ComponentCreationFlags
=> InstanceCreationFlags.ResolveOptionalParametersViaMocker;
Why:
- focused component-construction overrides are clearer than an ambient toggle
- constructor creation and invocation now share the same policy model
MockOptionalis obsolete and remains available only as a compatibility alias
Provider-first access
Tests carried forward from previous FastMoq versions often assumed Moq.Mock was the only meaningful tracked artifact.
Current guidance:
var fastMock = Mocks.GetOrCreateMock<IOrderRepository>();
var moqMock = fastMock.AsMoq();
When the mock should stay detached instead of joining the parent tracked graph, start from a detached provider-first handle and keep verification provider neutral there too:
var detachedRepository = MockingProviderRegistry.Default.CreateMock<IOrderRepository>();
detachedRepository.Setup(x => x.Load(123)).Returns(order);
MockingProviderRegistry.Default.Verify(
detachedRepository,
x => x.Load(123),
TimesSpec.Once);
If you want the shortest Moq-specific form in v4, use the provider-package shortcut methods directly on IFastMock<T>:
Mocks.GetOrCreateMock<IOrderRepository>()
.Setup(x => x.Load(123))
.Returns(order);
You can still inspect tracked models through MockModel.NativeMock or GetNativeMock(...) when you truly need the raw provider object.
Why:
- FastMoq is moving toward a provider-neutral core
- provider-specific convenience now belongs in provider packages instead of core
- raw
NativeMockaccess is still available, but it is no longer the best primary guidance when a typed provider extension exists
IFastMock<T> verify wrappers
Old helper patterns:
internal static void Verify<T>(this IFastMock<T> fastMock, Expression<Action<T>> expression, TimesSpec? times = null)
where T : class
=> MockingProviderRegistry.Default.Verify(fastMock, expression, times);
Or a provider-specific wrapper:
internal static void Verify<T, TResult>(this IFastMock<T> fastMock, Expression<Func<T, TResult>> expression, TimesSpec times)
where T : class
=> fastMock.AsMoq().Verify(expression, times.ToMoq());
Current guidance:
- call
Mocks.Verify<T>(...)directly when the mock is tracked by the currentMocker - call
MockingProviderRegistry.Default.Verify(...)directly when the handle is detached - do not add a new
IFastMock<T>.Verify(...)helper layer just to hide that distinction - when a repo-local
IFastMock<T>.Verify(...)wrapper still exists temporarily, prefer rewriting the call site directly toMocks.Verify<T>(...)orMockingProviderRegistry.Default.Verify(...)when that translation is mechanical
Why:
FMOQ0031covers wrappers that only forward to FastMoq's own verification APIs, and their direct wrapper call sites when the rewrite is mechanicalFMOQ0032covers wrappers that route verification back throughAsMoq().Verify(...), MoqVerify(...), orTimesSpectoTimesadapters, and their direct wrapper call sites when the rewrite is mechanical- those wrappers hide tracked-versus-detached intent and spread another verification surface through the suite without adding new behavior
Shared file system in MockerTestBase
Old pattern:
class SampleTests : MockerTestBase<SampleService>
{
public SampleTests() : base(mocker => new SampleService(new MockFileSystem().FileSystem))
{
}
}
Current guidance:
class SampleTests : MockerTestBase<SampleService>
{
public SampleTests() : base(mocker => new SampleService(mocker.GetFileSystem()))
{
}
}
Why:
FMOQ0033warns when aMockerTestBase-based flow creates a freshMockFileSystemonly to satisfy anIFileSystemslotGetFileSystem()keeps constructor injection and laterIFileSystemresolution on the same shared in-memory file system- keep a separate
MockFileSystemonly when the collaborator really needs the concreteMockFileSystemtype or the test intentionally needs an independent file system instance
Expected migration exceptions
Some migrated tests should intentionally stay on raw Moq. That is normal in v4.
.AsMoq().Verify(...) is the escape hatch, not the default just because the member returns Task. When the assertion can be expressed as Mocks.Verify<T>(...) for tracked mocks or MockingProviderRegistry.Default.Verify(...) for detached mocks, prefer the provider-neutral verification surface first.
For practical fallback patterns when you do not want to stay on Moq, see Provider Capabilities: Alternatives when a Moq feature is unavailable.
IMemoryCache and ICacheEntry
Property-setter and cache-entry tests are still a common Moq-only pocket because they often rely on SetupSet(...) or SetupAllProperties(). For ordinary interface collaborators, FastMoq now has first-party answers in AddPropertySetterCapture<TService, TValue>(...) and AddPropertyState<TService>(...), but ICacheEntry tests often still need the broader Moq shape.
Important migration rule:
- do not replace a tracked cache-entry helper with
AddType<ICacheEntry>(...)just because the immediate call site only needs an object - if the same helper later resolves
ICacheEntrythroughGetObject<T>(),GetRequiredTrackedMock<T>(),GetMockModel<T>(),AddPropertyState<TService>(...), orAddPropertySetterCapture<TService, TValue>(...), keep the tracked mock/helper path FMOQ0022exists specifically to catch this risky rewrite shape
var cacheEntry = Mocks.GetMock<ICacheEntry>();
cacheEntry.SetupAllProperties();
cacheEntry.SetupSet(x => x.Value = It.IsAny<object>());
Mocks.GetMock<IMemoryCache>()
.Setup(x => x.CreateEntry("orders"))
.Returns(cacheEntry.Object);
out / ref verification
If the test already depends on It.Ref<T>.IsAny or other Moq-specific out / ref verification forms, keeping raw Moq is usually the clearest move. Prefer GetOrCreateMock<T>().AsMoq() when that still reads cleanly, and leave GetMock<T>() in place only when preserving the legacy Moq shape avoids churn.
Mocks.GetMock<IParser>()
.Verify(x => x.TryResolve("tenant-a", out It.Ref<object>.IsAny), Times.Once);
Practical rule
- prefer provider-first APIs when they simplify the test
- keep raw Moq when setter-heavy, property-heavy, or
out/refheavy behavior would otherwise turn the migration into a rewrite - do not treat these cases as migration failures
Real migration examples
Constructor and default setup migration
Before:
Mocks.Initialize<IOrderRepository>(mock =>
mock.Setup(x => x.Load(123)).Returns(order));
After:
Mocks.GetOrCreateMock<IOrderRepository>()
.Setup(x => x.Load(123))
.Returns(order);
Logger verification migration
Before:
Mocks.GetMock<ILogger<OrderService>>()
.VerifyLogger(LogLevel.Information, "Processing started");
After:
Mocks.VerifyLogged(LogLevel.Information, "Processing started");
Selective verify migration
Before:
Mocks.GetMock<IOrderRepository>()
.Verify(x => x.Save(It.IsAny<Order>()), Times.Once);
After:
Mocks.Verify<IOrderRepository>(x => x.Save(FastArg.Any<Order>()), TimesSpec.Once);
Inside provider-first verification expressions, translate matcher helpers this way when the rewrite is mechanical:
It.IsAny<T>()becomesFastArg.Any<T>()It.Is<T>(predicate)becomesFastArg.Is<T>(predicate)It.IsAny<Expression<Func<T, bool>>>()becomesFastArg.AnyExpression<T>()
Analyzer notes:
FMOQ0024moves tracked verification off provider-nativeVerify(...)and onto provider-firstVerify(...)FMOQ0034rewrites the mechanical matcher cases above so the assertion stays provider-neutralFMOQ0035flags unsupported Moq matcher helpers that stay inside provider-firstVerify(...)without offering a code fix; in those cases, rewrite the assertion shape or keep the matcher on a deliberate local Moq-only verification path
Cache and property-setter edge case
Intentional Moq retention:
var cacheEntry = Mocks.GetMock<ICacheEntry>();
cacheEntry.SetupAllProperties();
cacheEntry.SetupSet(x => x.Value = It.IsAny<object>());
That is still a valid v4 migration outcome because the test depends on broader Moq-only cache-entry behavior.
For simpler interface collaborators where the real need is property state rather than cache-entry semantics, prefer the first-party helper instead:
var channel = Mocks.AddPropertyState<IOrderSubmissionChannel>();
CreateComponent();
await Component.SubmitAsync("order-42", expedited: true, CancellationToken.None);
channel.Mode.Should().Be("fast");
That helper keeps its original write-through behavior by default. If the migrated test needs proxy-local property state without mutating the previously wrapped instance, use AddPropertyState<IOrderSubmissionChannel>(PropertyStateMode.ProxyOnly) instead.
Known-type extensibility
Current guidance for framework-heavy types:
Mocks.AddKnownType<IFileSystem>(
directInstanceFactory: (_, _) => new MockFileSystem().FileSystem);
Why:
- known-type overrides are now scoped per
Mocker - extensions no longer need to rely on process-wide static mutation
Fluent scenario style
This is new repo-era surface rather than a 3.0.0 migration requirement, but it is now worth adopting for readable workflow-style tests:
Scenario
.With(() => { /* arrange */ })
.When(() => Component.Process())
.Then(() => { /* assert */ })
.Execute();
Use it when the test reads better as arrange or act or assert phases rather than as one large method body.
Inside MockerTestBase<TComponent>, prefer Scenario plus the parameterless With or When or Then overloads when Component is already available.
Use WhenThrows<TException>(...) when the act step is expected to fail but you still want trailing Then(...) assertions to run.
Use ExecuteThrows<TException>() or ExecuteThrowsAsync<TException>() when you want the thrown exception object back for direct inspection.