FastMoq Testing Guide
This guide documents the testing patterns that match FastMoq's current behavior in this repository. It is intentionally practical: use it when you need to decide which FastMoq API to reach for in a real test.
If you want reusable AI workflows for new tests, modernization, or migration work instead of writing prompts from scratch, see AI Prompt Templates.
Start Here
Use these rules first:
- Use MockerTestBase<TComponent> when you want FastMoq to create the component under test and manage its dependencies.
- If a
MockerTestBase<TComponent>test must force a specific constructor, overrideComponentConstructorParameterTypesfirst. UseCreateComponentActiononly when the test needs custom creation logic beyond selecting a signature. - Use Mocks.GetOrCreateMock<T>() when you want the normal FastMoq tracked mock path for a dependency.
- Use AddType(...) when you need to replace FastMoq's default resolution with a specific concrete type, factory, or fixed instance.
- Use
CreateInstanceByType(...)when directMockerusage must pick an exact constructor signature. Do not treatGetObject<T>()as the explicit constructor-selection API. - Use
CreateTypedServiceProvider(...),CreateTypedServiceScope(...),AddTypedServiceProvider(...), andAddTypedServiceScope(...)when framework code expects typed service-provider or service-scope behavior rather than a one-object-for-all-types shim. KeepAddServiceProvider(...)andAddServiceScope(...)for registering an existing provider or scope instance, or as compatibility aliases for older builder-based call sites. - If the constructor uses the same abstraction more than once under different DI service keys, use keyed mocks, keyed registrations, or explicit constructor injection for tests where dependency selection matters.
- Use AddKnownType(...) when a framework-style type needs special resolution or post-processing behavior.
- Use GetMockDbContext<TContext>() when testing EF Core contexts. Do not hand-roll DbContext setup unless you need behavior outside FastMoq's helper.
Prefer FastMoq-Owned Setup
When FastMoq already has a first-party helper for the dependency or framework primitive, prefer that helper over handwritten setup.
That is true even when the handwritten setup is technically valid. The FastMoq-owned helper usually reduces repeated plumbing, keeps the dependency graph in one place, and makes the test easier for other FastMoq users to recognize.
Use this decision table first:
| If the test wants... | Prefer... | Usually avoid first... |
|---|---|---|
| a tracked collaborator that you will arrange or verify | GetOrCreateMock<T>() | creating a mock and then re-registering mock.Instance with AddType(...) |
| a real fixed dependency instance | AddType(...) with the concrete instance | wrapping that instance in extra local indirection before handing it back to Mocker |
FastMoq's built-in real IFileSystem |
GetFileSystem(...) or GetObject<IFileSystem>() |
creating a fresh MockFileSystem only to satisfy an IFileSystem slot when the built-in shared one would do |
| typed DI or scope behavior for framework resolution | CreateTypedServiceProvider(...), AddTypedServiceProvider(...), CreateTypedServiceScope(...), or AddTypedServiceScope(...) |
ad hoc new ServiceCollection().BuildServiceProvider() setup when the typed helper can express the same shape |
| a framework-style built-in type with special behavior | the matching built-in helper or AddKnownType(...) | treating that dependency like an ordinary manual registration first |
Keep one dependency model per service
For one service in one test flow, pick one main model:
- tracked mock
- fixed concrete instance
- built-in known type
- typed provider registration
Avoid switching the same service back and forth between those models unless the test intentionally rebuilds the graph for a separate scenario.
For example, avoid this pattern:
var dependency = Mocks.GetOrCreateMock<IMyService>();
Mocks.AddType(dependency.Instance, replace: true);
That creates a tracked mock and then replaces resolution with the mock's instance as a fixed registration, which blurs the test's intent.
Reset and per-test state
In MockerTestBase<TComponent>, each test already starts from a fresh Mocker. Before adding extra setup only to reset state, check whether the current harness already gives you a clean per-test instance.
Use extra per-test instance creation only when the test truly needs an intentionally separate state boundary.
Choose The Narrowest Harness
Do not default every test to the heaviest FastMoq surface. Start with the smallest harness that matches the behavior under test.
| If the test needs... | Prefer... | Why |
|---|---|---|
| plain constructor or pure method behavior with no FastMoq-managed resolution | direct construction plus a plain fake, stub, or explicit dependency instance | keeps the test local and makes non-FastMoq behavior obvious |
| a few DI-managed collaborators without a reusable base class | direct Mocker usage |
good fit when you want FastMoq resolution without introducing a harness type |
| repeated component tests where FastMoq should create the subject and own the dependency graph | MockerTestBase<TComponent> | keeps creation, tracked verification, and shared setup in one place |
| framework-owned HTTP, ASP.NET, Azure Functions, or service-provider plumbing | the matching first-party helper package | avoids hand-rolled framework shims that analyzers now flag directly |
| real host, container, database, or transport contracts | a separate integration or contract test with explicit real infrastructure | keeps the infrastructure boundary visible instead of hiding it behind partial mocks |
Practical rules:
- keep plain DI tests plain when direct construction is clearer than a reusable harness
- use FastMoq to remove repeated plumbing, not to obscure the real boundary under test
- keep real infrastructure tests thin and explicit instead of turning them into half-real component tests
Core Mental Model
FastMoq has three distinct resolution paths:
- Type mapping: explicit registrations added with AddType(...).
- Known types: built-in framework helpers for things like
IFileSystem,HttpClient,DbContext, andHttpContextpatterns. - Auto-mock / auto-create: default mock creation and constructor injection when no explicit mapping exists.
That distinction matters because the API choice communicates intent.
Local Wrapper Boundary
Repo-local wrappers can still be useful, but they should compress repeated setup rather than create another verification abstraction on top of FastMoq.
Keep or add a local wrapper when it:
- builds the same request, context, or provider shape across many tests
- re-points an existing suite toward first-party FastMoq helpers with lower call-site churn
- standardizes repeated setup that would otherwise stay verbose and mechanical
Avoid wrapper layers when they:
- only forward to
Mocks.Verify(...),MockingProviderRegistry.Default.Verify(...), orVerifyNoOtherCalls(...) - route provider-neutral verification back through
AsMoq().Verify(...)or provider-specificTimesadapters - hide whether the handle being verified is tracked or detached
Analyzer note:
FMOQ0031andFMOQ0032are intentionally opinionated here: they discourage verification-only wrappers because those wrappers blur tracked-versus-detached intent without adding behavior
Equality intent in matcher-style expressions
When you do step into provider-native matcher predicates, make the equality intent obvious instead of relying on readers to infer what == means for a particular type.
- record or value-object arguments can legitimately use value equality, for example
command => command == expectedCommandorcommand => command.Equals(expectedCommand)when the type is intentionally value-based - mutable class arguments usually need either identity or property-based intent, for example
session => ReferenceEquals(session, expectedSession)when the exact instance matters - if neither pure value equality nor identity is the real requirement, match the stable property that explains the behavior, such as
message => message.Id == expectedId
That same rule applies whether the arrange side uses It.Is(...), Arg.Is(...), or another provider-native matcher surface.
GetOrCreateMock<T>() vs AddType(...)
These are not interchangeable.
Use GetOrCreateMock<T>() when
- You want the dependency to stay on the default auto-mock path.
- You only need to arrange or verify behavior.
- You want the dependency tracked as the mock FastMoq would have created anyway.
The example below assumes the Moq provider extensions are in use for the arrange step. The tracked-mock concept is provider-first, but the .Setup(...) syntax itself is provider-specific. For exact-call fixed results that can stay on a shared FastMoq surface, prefer AddMethodResult(...) or AddMethodResultAsync(...) before dropping into provider-native setup syntax. See Provider Capabilities when you need the equivalent arrangement style for another provider.
var repoMock = Mocks.GetOrCreateMock<IOrderRepository>();
repoMock.Setup(x => x.Load(123)).Returns(order);
When AddType(...) is the right tool
- You need a specific implementation rather than a default mock.
- You need to control constructor arguments or non-public construction.
- You want a fixed singleton-like instance returned for that type.
- You need to disambiguate multiple concrete implementations.
Mocks.AddType<IClock>(_ => new FakeClock(DateTimeOffset.Parse("2026-04-01T12:00:00Z")));
Practical rule
If the dependency is still conceptually a mock, prefer GetOrCreateMock<T>(). If you are changing how the type is resolved, prefer AddType(...).
Migration guardrail:
- do not rewrite a tracked helper to AddType(...) when the same service still flows through
GetObject<T>(),GetRequiredTrackedMock<T>(),GetMockModel<T>(),AddPropertyState<TService>(...), orAddPropertySetterCapture<TService, TValue>(...) - that is still a tracked FastMoq dependency, not a concrete type-map override
Common verification counts
When the assertion is mechanically “once” or “never”, prefer the explicit provider-first wrappers over repeating TimesSpec boilerplate:
Mocks.VerifyCalledOnce<IOrderGateway>(x => x.Publish("order-42"));
Mocks.VerifyNotCalled<IOrderGateway>(x => x.Publish("order-99"));
The same shorthand exists for captured logger assertions:
Mocks.VerifyLoggedOnce(LogLevel.Information, "Submitted order");
Mocks.VerifyNotLogged(LogLevel.Error, "Unhandled failure");
When the verification only cares that a method was called and every argument can stay a wildcard, prefer the delegate-selector overload for compiler-verified member selection:
Mocks.VerifyAnyArgs<IArmDeploymentGateway, Func<ResourceGroupResource, Dictionary<string, ArmParamValue>, Stream, Stream, bool, Task>>(
gateway => gateway.CreateDeploymentAsync,
TimesSpec.Once);
If the method is overloaded, fall back to the method-name overload with explicit parameter types:
Mocks.VerifyAnyArgs<IArmDeploymentGateway>(
nameof(IArmDeploymentGateway.CreateDeploymentAsync),
TimesSpec.Once,
typeof(ResourceGroupResource),
typeof(Dictionary<string, ArmParamValue>),
typeof(Stream),
typeof(Stream),
typeof(bool));
If the test wants a count shorthand without spelling out TimesSpec, the provider-first surface now exposes explicit wrappers beyond once and never:
Mocks.VerifyCalledExactly<IOrderGateway>(gateway => gateway.Publish("order-42"), 2);
Mocks.VerifyCalledAtLeastAnyArgs<IArmDeploymentGateway, Func<ResourceGroupResource, Dictionary<string, ArmParamValue>, Stream, Stream, bool, Task>>(
gateway => gateway.CreateDeploymentAsync,
1);
Shared verification selection matrix
Use the smallest shared verification shape that still expresses the intent clearly:
| If the assertion is... | Prefer... | ScenarioBuilder path | Fall back when... |
|---|---|---|---|
| one exact expected call with explicit argument intent | Verify(...) with TimesSpec and FastArg markers where needed |
.Verify<T>(...) |
the assertion fundamentally depends on provider-native semantics |
| a once / never / exact / at-least / at-most count | VerifyCalledOnce(...), VerifyNotCalled(...), VerifyCalledExactly(...), VerifyCalledAtLeast(...), or VerifyCalledAtMost(...) |
keep .Verify<T>(..., TimesSpec...) inside ScenarioBuilder; the count wrappers are outer Mocks / MockingProviderRegistry conveniences |
the test is really about order, sequences, or another provider-specific behavior |
| a method-level wildcard assertion where every argument can stay flexible | VerifyAnyArgs(...) and the matching *AnyArgs(...) count helpers |
use .Verify<T>(...) inline when the scenario already has a readable verification expression; use VerifyAnyArgs(...) around execution when wildcard matching is the main point |
overloaded or provider-bound behavior needs a provider-native path |
| no-other-calls on a tracked or detached dependency | VerifyNoOtherCalls(...) |
.VerifyNoOtherCalls<T>() |
the suite intentionally preserves a provider-native no-other-calls surface |
| logger assertions | VerifyLogged(...), VerifyLoggedOnce(...), and VerifyNotLogged(...) when logger capture is supported |
call them on Mocks around scenario execution |
the provider does not support logger capture |
| call order, event sequencing, protected members, or other provider-bound verification | provider-native verification APIs | no shared ScenarioBuilder wrapper | the provider-specific feature is the behavior under test |
ScenarioBuilder note:
- the supported inline verification hooks are
.Verify<T>(...)and.VerifyNoOtherCalls<T>() - if a test wants the convenience wrappers such as
VerifyCalledExactly(...),VerifyCalledAtLeastAnyArgs(...), orVerifyLoggedOnce(...), keep those onMocksorMockingProviderRegistryaround scenario execution instead of expecting separate ScenarioBuilder overloads
When a provider-neutral test needs a reusable observability dump for debugging, capture a snapshot from the current mocker instead of reaching into provider-native mock internals:
var snapshot = Mocks.CreateDiagnosticsSnapshot();
var debugView = snapshot.ToDebugView();
var json = snapshot.ToJson();
That snapshot surfaces the current tracked mocks, keyed tracked mocks, observed constructor selections, keyed and unkeyed instance registrations, and captured log entries through one FastMoq-owned diagnostics object.
For detached mocks, use the matching helpers on MockingProviderRegistry:
var emailGateway = Mocks.CreateStandaloneFastMock<IEmailGateway>();
MockingProviderRegistry.VerifyCalledOnce(emailGateway, x => x.Send("finance@contoso.test"));
MockingProviderRegistry.VerifyNotCalled(emailGateway, x => x.Send("audit@contoso.test"));
MockingProviderRegistry.VerifyNoOtherCalls(emailGateway);
Exact-call fixed results
When a test only needs a fixed return value, exact-call completion, exact-call callback, or exact-call exception for one collaborator call, prefer AddMethodResult(...), AddMethodResultAsync(...), AddMethodCompletionAsync(...), AddMethodCallback(...), AddMethodCallbackAsync(...), AddMethodException(...), or AddMethodExceptionAsync(...) over provider-native Setup(...).Returns(...), ReturnsAsync(...), Callback(...), or Throws(...) chains:
var gateway = Mocks.AddMethodResult<IOrderGateway, Order?>(
x => x.Load("order-42"),
expectedOrder);
gateway.Load("order-42").Should().BeSameAs(expectedOrder);
Those helpers intentionally stay narrow:
- they support direct method-call expressions only
- they are aimed at exact-call fixed results, exact-call
Taskcompletions, exact-call callbacks, and exact-call exceptions, including simple asyncTask<T>results throughAddMethodResultAsync(...), completedTaskresponses throughAddMethodCompletionAsync(...), side-effect callbacks throughAddMethodCallback(...)/AddMethodCallbackAsync(...), and faultedTask/Task<T>responses throughAddMethodExceptionAsync(...) - they do not try to replace broader provider-native setup chains, exception orchestration, or advanced callback behavior
Shared setup selection matrix
| If the arranged behavior is... | Prefer... | Keyed-service note | Fall back when... |
|---|---|---|---|
one exact-call fixed return, fixed Task<T> result, completed Task, callback, or exception on an interface collaborator |
AddMethodResult(...), AddMethodResultAsync(...), AddMethodCompletionAsync(...), AddMethodCallback(...), AddMethodCallbackAsync(...), AddMethodException(...), or AddMethodExceptionAsync(...) |
these helpers currently wrap the currently resolved service and do not expose keyed-specific overloads | the behavior needs sequencing, conditional setup, advanced callbacks, or a class target |
| simple stateful interface properties | AddPropertyState<TService>(...) |
this helper currently wraps the currently resolved service and does not expose keyed-specific overloads | the target is a class, the property behavior is broader than simple state, or a keyed role needs its own explicit fake |
| capturing assignments to one interface property | AddPropertySetterCapture<TService, TValue>(...) |
this helper currently wraps the currently resolved service and does not expose keyed-specific overloads | setter interception is not the behavior under test, or the target is a class |
| one fixed keyed dependency instance | AddKeyedType(...) and GetKeyedObject<T>(...) |
keyed registrations are first-class here | the dependency is still conceptually a mock that should stay tracked |
Keyed practical rule:
- preserve the keyed DI role first by choosing keyed mocks or keyed fixed instances
- if you still need exact-call or property-state behavior for that keyed role, use a keyed tracked mock with provider-native setup or a keyed fake/stub instead of expecting the unkeyed helper overloads to retarget themselves
Analyzer note:
FMOQ0022warns when anAddType(...)rewrite comes from a tracked mock/object path and the same file still uses tracked-resolution APIs or property helpers for that service
Ambiguous interface and detached fallback decision table
Current limitation:
GetOrCreateMock<T>()can throwAmbiguousImplementationExceptionfor interface types when multiple concrete implementations are loaded and FastMoq cannot infer which implementation the test means
Treat that as a resolution-shape decision, not as a reason to keep retrying unkeyed tracked mocks.
| If the test needs... | Preferred path | Why |
|---|---|---|
one tracked dependency in the current Mocker |
GetOrCreateMock<T>() | default tracked path reused by later GetObject<T>() and Verify(...) calls |
| a second distinct role of the same abstraction in the tracked graph | keyed GetOrCreateMock<T>(), AddKeyedType(...), or CreateFastMock<T>() when you intentionally want a new tracked registration |
keeps public vs private or primary vs secondary roles separate inside the parent Mocker |
| a detached collaborator or manual wiring outside the tracked graph | CreateStandaloneFastMock<T>() or MockingProviderRegistry.Default.CreateMock<T>() |
creates a provider-first handle that is not registered as the parent tracked dependency |
| a specific concrete implementation behind an ambiguous interface | AddType(...) or AddKeyedType(...) | makes the intended implementation explicit instead of asking FastMoq to guess |
Tracked vs Standalone Fast Mocks
Use GetOrCreateMock<T>() for the normal tracked dependency inside the current Mocker.
Use CreateFastMock<T>() when you intentionally want to create a new tracked registration immediately.
Use CreateStandaloneFastMock<T>() when the test needs a detached provider-selected mock handle that will be wired manually and must not be added to the parent tracked collection.
Important behavior differences:
GetOrCreateMock<T>()reuses an existing tracked mock for the same unkeyed type or service key.CreateFastMock<T>()registers the new mock in the currentMocker; for unkeyed mocks it throws if that type is already tracked.CreateStandaloneFastMock<T>()does not register the mock, so it is the provider-first replacement for legacy detached mock creation.- if you need two independently tracked collaborators of the same abstraction in one component graph, use keyed mocks or a separate
Mockerinstead of a second unkeyedCreateFastMock<T>()call.
var trackedGateway = Mocks.GetOrCreateMock<IEmailGateway>();
var detachedGateway = Mocks.CreateStandaloneFastMock<IEmailGateway>();
var manuallyWiredService = new CheckoutService(detachedGateway.Instance);
Detached verification and async-returning members
Tracked mocks use Mocks.Verify<T>(...) because the Mocker already owns the registration.
Detached mocks use MockingProviderRegistry.Default.Verify(...) because the handle is not registered in the parent tracked collection.
That provider-neutral detached verification shape still works for methods that return Task or Task<T> when the verification expression intentionally discards the return value:
var emailGateway = MockingProviderRegistry.Default.CreateMock<IEmailGateway>();
var service = new CheckoutService(emailGateway.Instance);
await service.NotifyFinanceAsync(CancellationToken.None);
MockingProviderRegistry.Default.Verify(
emailGateway,
x => x.SendAsync("finance@contoso.test", CancellationToken.None),
TimesSpec.Once);
MockingProviderRegistry.Default.VerifyNoOtherCalls(emailGateway);
Use tracked verification when the dependency lives in the current Mocker. Use detached verification when the test created an out-of-band IFastMock<T> handle directly.
Use CreateFastMock<T>() only when you want the new registration to become the tracked mock returned later by GetOrCreateMock<T>().
Typed IServiceProvider Helpers
Framework-heavy tests should not fake IServiceProvider with a single mocked GetService(Type) callback that returns the same object for every request.
That shape is convenient, but it hides real failures when code asks for multiple service types such as ILoggerFactory, IOptions<T>, or a concrete singleton.
Use CreateTypedServiceProvider(...) when you need a real provider instance:
var instanceServices = Mocks.CreateTypedServiceProvider(services =>
{
services.AddLogging();
services.AddOptions();
services.AddSingleton(new Uri("https://fastmoq.dev"));
});
If framework-owned resolution should fall back to the current Mocker for unregistered collaborators, opt in explicitly:
var instanceServices = Mocks.CreateTypedServiceProvider(
services => services.AddLogging(),
includeMockerFallback: true);
Use AddTypedServiceProvider(...) when the system under test resolves IServiceProvider or IServiceScopeFactory from the current Mocker and the provider should be built from service registrations:
Mocks.AddTypedServiceProvider(services =>
{
services.AddLogging();
services.AddSingleton(new WidgetClock());
});
AddTypedServiceProvider(...) registers the typed provider itself and, when the built container exposes them, also registers IServiceScopeFactory and IServiceProviderIsService for the current Mocker.
Use AddServiceProvider(...) when you already have an IServiceProvider instance to register, or when an existing test still uses the older builder-based alias and call-site churn is not worth a separate rewrite.
Use CreateTypedServiceScope(...) when the test needs an actual scope instance or wants to verify scoped lifetimes directly:
using var scope = Mocks.CreateTypedServiceScope(services =>
{
services.AddScoped<ScopedWidgetContext>();
});
var scopedService = scope.ServiceProvider.GetRequiredService<ScopedWidgetContext>();
Use AddTypedServiceScope(...) when the current Mocker should build and expose a scope and its scope-owned provider:
Mocks.AddTypedServiceScope(services =>
{
services.AddScoped<ScopedProbe>();
}, replace: true);
Use AddServiceScope(...) when the current Mocker should expose an existing scope or an existing provider through a fixed scope:
using var scope = Mocks.CreateTypedServiceScope(services =>
{
services.AddScoped<ScopedWidgetContext>();
});
Mocks.AddServiceScope(scope, replace: true);
If a helper already has a real provider and only needs a fixed scope plus a matching scope factory, use the provider-backed overload:
var provider = Mocks.CreateTypedServiceProvider(services =>
{
services.AddScoped<ScopedWidgetContext>();
});
Mocks.AddServiceScope(provider, replace: true);
That overload exposes the supplied IServiceProvider, a fixed IServiceScope, and an IServiceScopeFactory that returns that registered scope, which maps well to older scope.SetupGet(x => x.ServiceProvider) and scopeFactory.Setup(x => x.CreateScope()) shim patterns.
If a constructor takes IServiceScopeFactory, prefer this shape:
var fileSystem = Mocks.GetFileSystem();
Mocks.AddServiceProvider(services =>
{
services.AddLogging();
services.AddOptions();
services.AddSingleton<IFileSystem>(fileSystem);
services.AddSingleton<ArchiveService>();
}, replace: true);
var scopeFactory = Mocks.GetRequiredObject<IServiceScopeFactory>();
Reuse GetFileSystem() when you want FastMoq's shared in-memory file system to stay aligned across constructor injection and the typed provider. Use AddType<IFileSystem>(...) only when you intentionally want to replace that shared instance with a custom file system.
Instead of building a provider manually and registering only provider.GetRequiredService<IServiceScopeFactory>(), keep the full typed provider registered so constructor injection, nested framework resolution, and service-scope behavior stay aligned.
When framework code should resolve a mix of real DI registrations and normal FastMoq collaborators, use includeMockerFallback: true on CreateTypedServiceProvider(...), CreateTypedServiceScope(...), AddTypedServiceProvider(...), or AddTypedServiceScope(...).
For Azure-oriented tests that also need configuration defaults, prefer CreateAzureServiceProvider(...) or AddAzureServiceProvider(...) from FastMoq.Azure.DependencyInjection instead of repeating AddLogging(), AddOptions(), and IConfiguration setup in every test.
Use CreateFunctionContextInstanceServices(...) and AddFunctionContextInstanceServices(...) for Azure Functions worker tests instead of hand-writing FunctionContext.InstanceServices plumbing:
using FastMoq.AzureFunctions.Extensions;
Mocks.AddFunctionContextInstanceServices(services =>
{
services.AddSingleton(new WidgetClock());
});
var context = Mocks.GetObject<FunctionContext>();
var clock = context.InstanceServices.GetRequiredService<WidgetClock>();
For Durable orchestration tests, keep replay-safe logging on the normal FastMoq logger capture path instead of building a second assertion layer:
using FastMoq.AzureFunctions.Extensions;
Mocks.AddTaskOrchestrationReplaySafeLogging(replace: true);
var orchestrationContext = Mocks.GetObject<TaskOrchestrationContext>();
var logger = orchestrationContext.CreateReplaySafeLogger<ShipOrderOrchestrator>();
logger.LogInformation("starting orchestration");
Mocks.VerifyLogged(LogLevel.Information, "starting orchestration", TimesSpec.Once);
When you want replay-safe log suppression behavior, pass isReplaying: true and assert against the existing Mocks.LogEntries surface.
For HTTP-trigger tests, use CreateHttpRequestData(...) and CreateHttpResponseData(...) to build concrete worker request or response objects instead of hand-rolling HttpRequestData and HttpResponseData doubles:
using FastMoq.AzureFunctions.Extensions;
var request = Mocks.CreateHttpRequestData(builder => builder
.WithMethod("POST")
.WithUrl("https://localhost/api/widgets?mode=test")
.WithHeader("x-correlation-id", "123")
.WithJsonBody(new CreateWidgetRequest
{
Name = "demo",
}));
var payload = await request.ReadFromJsonAsync<CreateWidgetRequest>();
var response = request.CreateResponse();
Use ReadBodyAsStringAsync(...) and ReadBodyAsJsonAsync<T>(...) when you want to assert request or response bodies without manually rewinding the underlying stream.
Package note:
CreateTypedServiceProvider(...),AddTypedServiceProvider(...), andAddServiceProvider(...)remain part ofFastMoq.CoreCreateTypedServiceScope(...),AddTypedServiceScope(...), andAddServiceScope(...)remain part ofFastMoq.Core- direct
FastMoq.Coreconsumers should addFastMoq.AzureFunctionsand importFastMoq.AzureFunctions.Extensionsbefore usingCreateFunctionContextInstanceServices(...),AddFunctionContextInstanceServices(...),CreateHttpRequestData(...), orCreateHttpResponseData(...) AddTaskOrchestrationReplaySafeLogging(...)also lives inFastMoq.AzureFunctions; it depends only onMicrosoft.DurableTask.Abstractions, so tests that only need replay-safe logger creation do not need the heavier Durable Functions worker extension package just to verify orchestration logsMocker.AddTaskOrchestrationReplaySafeLogging(...)can register a concrete replay-safe orchestration context before resolution on Moq, NSubstitute, or reflection paths; the trackedIFastMockoverload is narrower and currently requires a provider that supports FastMoq's tracked-property configuration contract for the protected Durable logger factory getter, which the built-in Moq provider supports today- the aggregate
FastMoqpackage includes the Azure Functions helper package already
Analyzer note:
FMOQ0013warns on directGetOrCreateMock<IServiceProvider>(),GetMock<IServiceProvider>(),GetRequiredMock<IServiceProvider>(),IServiceScopeFactoryorIServiceScopeshims, and manual scope-factory extraction so those patterns migrate toward the typed helper path.
Construction APIs
FastMoq still supports the older entry points:
For new code, prefer the flags-based constructor overloads when you need an explicit override:
var component = Mocks.CreateInstanceByType<MyComponent>(
InstanceCreationFlags.AllowNonPublicConstructorFallback,
typeof(int),
typeof(string));
If constructor selection should be restricted to public constructors, use the explicit flag:
var component = Mocks.CreateInstance<MyComponent>(InstanceCreationFlags.PublicConstructorsOnly);
If you want to change the default constructor-fallback policy for the whole Mocker instance, use:
Mocks.Policy.DefaultFallbackToNonPublicConstructors = false;
Internal And Protected Components
FastMoq can create components that use internal or protected constructors, but C# still enforces compile-time accessibility on the test type itself.
If a test framework requires the outward test class to remain public, use a public test class plus an internal FastMoq harness:
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;
}
internal sealed class InternalOrderRules
{
internal InternalOrderRules()
{
}
public bool IsPriority(string code)
{
return code == "P1";
}
}
If the component under test lives in another assembly, expose it to the test assembly with InternalsVisibleTo or an equivalent visibility rule first.
FastMoq handles constructor resolution and injection at runtime; it does not bypass the compiler rule that prevents public class MyTests : MockerTestBase<InternalType> when InternalType is less accessible than the public test type.
Explicit Constructor Selection In Tests
When a test needs a specific constructor, prefer a test-side override first.
That keeps constructor choice inside the test harness and avoids changing production code only to satisfy test setup.
If the selected constructor depends on IServiceProvider or IServiceScopeFactory, pair the constructor-selection hook with AddTypedServiceProvider(...) from the earlier typed-provider section instead of registering only a manually extracted scope factory.
For MockerTestBase<TComponent>, override ComponentConstructorParameterTypes when you want a specific signature but still want the default FastMoq creation path:
internal sealed class OrderRulesTestBase : MockerTestBase<OrderRules>
{
protected override Type?[]? ComponentConstructorParameterTypes
=> new Type?[] { typeof(IFileSystem), typeof(string) };
}
Use CreateComponentAction when the test needs full control over creation, custom argument values, or logic that cannot be expressed as a parameter-type signature:
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 still works, but the override-based hook is the better default for new test bases because it keeps constructor intent local to the derived type.
Constructor Ambiguity And Preferred Constructors
FastMoq still throws by default when multiple same-rank constructors remain viable after candidate filtering. That preserves the current behavior for existing suites.
If you control the production type and want it to advertise a preferred default constructor for all callers, you can mark it explicitly with [PreferredConstructor].
That is secondary to the test-side hooks above and is most useful when constructor preference is part of the component's intended public shape, not just a test need:
internal sealed class OrderRules
{
[PreferredConstructor]
public OrderRules()
{
}
public OrderRules(IFileSystem fileSystem)
{
}
}
If you want FastMoq to fall back to a parameterless constructor when ambiguity remains, opt in explicitly through policy or a per-call flag:
Mocks.Policy.DefaultConstructorAmbiguityBehavior = ConstructorAmbiguityBehavior.PreferParameterlessConstructor;
var component = Mocks.CreateInstance<MyComponent>(
InstanceCreationFlags.PreferParameterlessConstructorOnAmbiguity);
FastMoq writes constructor-selection diagnostics into Mocks.LogEntries when [PreferredConstructor] is used or when ambiguity fallback is applied.
Optional Constructor And Method Parameters
FastMoq now has an explicit option for optional-parameter behavior.
Default behavior:
- optional parameters use their declared default value when one exists
- otherwise FastMoq passes
null
If you want FastMoq to resolve optional parameters through the normal mock/object pipeline, opt in explicitly.
Constructor creation
Mocks.OptionalParameterResolution = OptionalParameterResolutionMode.ResolveViaMocker;
var component = Mocks.CreateInstance<MyComponent>();
MockerTestBase<TComponent>
For SUT creation through MockerTestBase<TComponent>, override ComponentCreationFlags:
protected override InstanceCreationFlags ComponentCreationFlags
=> InstanceCreationFlags.ResolveOptionalParametersViaMocker;
Delegate or reflected invocation
Use InvocationOptions when calling helpers that fill omitted method parameters:
var result = Mocks.CallMethod<MyResult>(new InvocationOptions
{
OptionalParameterResolution = OptionalParameterResolutionMode.ResolveViaMocker,
}, (Func<IMyDependency?, MyResult>)CreateResult);
var result = Mocks.InvokeMethod(
new InvocationOptions
{
OptionalParameterResolution = OptionalParameterResolutionMode.ResolveViaMocker,
},
target,
nameof(TargetType.Run));
If reflected method fallback should be controlled explicitly, use:
var result = Mocks.InvokeMethod(
new InvocationOptions
{
FallbackToNonPublicMethods = false,
},
target,
nameof(TargetType.Run));
If you want to change the default reflected-method fallback policy for the whole Mocker instance, use:
Mocks.Policy.DefaultFallbackToNonPublicMethods = false;
MockOptional is now obsolete and should be treated only as a compatibility alias for OptionalParameterResolutionMode.ResolveViaMocker.
Built-In Known Types
FastMoq includes built-in handling for a small set of framework-heavy types.
That built-in resolution policy is now explicit. FailOnUnconfigured no longer silently changes which built-ins are available by itself.
Use EnabledBuiltInTypeResolutions when you want to override the built-in defaults for a Mocker instance:
Mocks.Policy.EnabledBuiltInTypeResolutions =
BuiltInTypeResolutionFlags.FileSystem |
BuiltInTypeResolutionFlags.HttpClient |
BuiltInTypeResolutionFlags.Uri |
BuiltInTypeResolutionFlags.DbContext;
Strict compatibility still stamps the older strict-era defaults onto that policy surface, but new code can now control those pieces independently.
Mock Creation Defaults
For provider-backed mock creation, use the Mocker-level default when you want new mocks to be created as strict or loose without depending on the broader compatibility bundle:
Mocks.Policy.DefaultStrictMockCreation = true;
This affects provider-backed and legacy mock creation helpers. It does not apply to GetMockDbContext<TContext>(), which remains on the supported DbContext helper behavior.
If you are using MockerTestBase<TComponent>, apply the same defaults before component creation with:
protected override Action<MockerPolicyOptions>? ConfigureMockerPolicy => policy =>
{
policy.DefaultStrictMockCreation = true;
};
IFileSystem
Use GetFileSystem() when you want FastMoq's shared in-memory file system explicitly. GetObject<IFileSystem>() resolves that same shared instance when built-in file-system resolution is enabled and you have not already registered or created an IFileSystem dependency.
Use this when you want a real in-memory file system quickly:
var fileSystem = Mocks.GetFileSystem();
fileSystem.File.WriteAllText("/tmp/test.txt", "hello");
If you want mock arrangement and verification instead, stay on the mock path:
Mocks.GetOrCreateMock<IFileSystem>()
.Setup(x => x.File.Exists("orders.json"))
.Returns(true);
FastMoq can automatically provide a built-in IFileSystem backed by its shared in-memory MockFileSystem when the built-in file-system resolution is enabled and you have not already registered IFileSystem explicitly.
Reach for AddType<IFileSystem>(...) only when you intentionally need to replace that shared file system with a custom or isolated instance.
If you need the wider filesystem abstraction family (IFile, IPath, IDirectory, and related factories) to resolve coherently alongside that shared file system, call AddFileSystemAbstractionMapping().
HttpClient
FastMoq has a built-in HttpClient helper path. Every new Mocker starts with a shared HttpClient plus a lightweight IHttpClientFactory compatibility registration backed by the same provider-neutral handler.
Use that built-in path when the subject depends on HttpClient directly or only needs IHttpClientFactory.CreateClient(...) to hand back a client. Prefer WhenHttpRequest(...) and WhenHttpRequestJson(...) for provider-neutral response setup instead of manually composing handlers for every test.
Use ConfigureHttpClient(...) when the test only needs to update the built-in compatibility factory and default response behavior before resolving clients later through constructor injection, GetObject<IHttpClientFactory>(), or normal CreateClient(...) calls. Use CreateHttpClient(...) when the test also wants an immediate client instance back from the same helper call.
Use GetObject<IHttpClientFactory>(), GetRequiredObject<IHttpClientFactory>(), or normal constructor injection when you want that built-in factory. Do not call GetOrCreateMock<IHttpClientFactory>() unless you intentionally want to replace the built-in compatibility factory with a tracked mock.
The built-in compatibility factory accepts the requested client name but does not apply per-name configuration.
If you call ConfigureHttpClient(...) or CreateHttpClient(...) again later with a different base address or default response, FastMoq updates that built-in compatibility factory and handler to match the latest helper call.
If you intentionally replace IHttpClientFactory with GetOrCreateMock<IHttpClientFactory>() or AddType<IHttpClientFactory>(...), that replacement wins and you own CreateClient(...) setup yourself.
If the subject depends on named-client, typed-client, or AddHttpClient(...) configuration semantics, register your own IHttpClientFactory or typed provider-backed container instead of relying on the built-in compatibility factory.
DbContext
Use GetMockDbContext<TContext>() as the default entry point.
If you consume the aggregate FastMoq package, the database helpers remain available with the same API shape. If you consume FastMoq.Core directly, install FastMoq.Database for EF-specific helpers.
When you need to choose between pure mock behavior and a real EF in-memory context, use GetDbContextHandle<TContext>(...) with DbContextHandleOptions<TContext>. The default mode remains MockedSets, and GetMockDbContext<TContext>() is now the convenience wrapper over that default.
protected override Action<Mocker> SetupMocksAction => mocker =>
{
var dbContextMock = mocker.GetMockDbContext<ApplicationDbContext>();
dbContextMock.Object.Database.EnsureCreated();
};
Recommended pattern:
- Create the context mock with GetMockDbContext<TContext>().
- Seed test data through the resolved context object or
dbContextMock.Objectbefore calling the system under test. - If you need the tracked provider-first handle for the same context, call
GetOrCreateMock<TContext>()after the helper has tracked it; the returned mock exposes that same tracked context.
This is the supported path for EF Core tests in this repo. It keeps DbSet setup and context creation aligned with the framework's existing helper behavior.
Real in-memory example:
var handle = mocker.GetDbContextHandle<ApplicationDbContext>(new DbContextHandleOptions<ApplicationDbContext>
{
Mode = DbContextTestMode.RealInMemory,
});
handle.Context.Database.EnsureCreated();
Known-type override example:
Mocks.AddKnownType<ApplicationDbContext>(
managedInstanceFactory: (mocker, _) => mocker.GetDbContextHandle<ApplicationDbContext>(new DbContextHandleOptions<ApplicationDbContext>
{
Mode = DbContextTestMode.RealInMemory,
}).Context,
replace: true);
Use that pattern when the test needs the context to resolve through FastMoq's known-type pipeline rather than through a one-off AddType(...) mapping.
HttpContext and IHttpContextAccessor
FastMoq applies built-in setup for HttpContext, IHttpContextAccessor, and HttpContextAccessor so common web tests have a usable context object without repetitive setup.
When you want explicit test setup for headers, query strings, or authenticated users, use the FastMoq.Web.Extensions helpers instead of wiring those pieces by hand.
Package note:
- if your project references the aggregate
FastMoqpackage, the web helpers are already available - if your project references
FastMoq.Coredirectly, addFastMoq.Webbefore usingFastMoq.Web.Extensions - for the broader package-choice rules, see Getting Started installation and package choices
Controller Testing
For controller and request-driven tests, prefer building the request shape explicitly with the web helpers.
using FastMoq.Web.Extensions;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
public class OrdersControllerTests : MockerTestBase<OrdersController>
{
protected override Action<Mocker>? SetupMocksAction => mocker =>
{
var httpContext = mocker
.CreateHttpContext("Admin")
.SetRequestHeader("X-Correlation-Id", "corr-123")
.SetQueryParameter("includeInactive", "true");
mocker.AddHttpContextAccessor(httpContext);
mocker.AddHttpContext(httpContext);
};
[Fact]
public async Task Get_ShouldReturnOrders_WhenRequestIsValid()
{
var requestContext = Mocks.GetObject<HttpContext>();
Component.ControllerContext = Mocks.CreateControllerContext(requestContext);
Mocks.GetOrCreateMock<IOrderService>()
.Setup(x => x.GetOrdersAsync(true, It.IsAny<CancellationToken>()))
.ReturnsAsync([new OrderDto { Id = 42 }]);
var result = await Component.Get();
result.GetOkObjectResult()
.Value
.Should()
.NotBeNull();
}
}
Practical rules:
- Use CreateHttpContext(...) when you need a reusable request object for middleware or accessors.
- Use AddHttpContext(...) or AddHttpContextAccessor(...) when the system under test resolves those types from DI.
- Use
SetRequestHeader(...),SetRequestHeaders(...),SetQueryString(...),SetQueryParameter(...), orSetQueryParameters(...)to make request intent obvious in the test. - Use CreateControllerContext(...) when the controller itself reads from
ControllerContext.HttpContext.User. - Use GetOkObjectResult(), GetBadRequestObjectResult(), GetConflictObjectResult(), and GetObjectResultContent<T>() to keep result assertions short.
Quick decision table:
| If the test needs... | Prefer... | Why |
|---|---|---|
| role-only authenticated user setup | SetupClaimsPrincipal(params roles) or CreateControllerContext(params roles) |
Fast path for common controller and request tests |
| exact custom claims | SetupClaimsPrincipal(claims, options) |
Use IncludeDefaultIdentityClaims = false when exact claim preservation matters |
controller reads ControllerContext.HttpContext.User |
CreateControllerContext(...) |
Keeps controller user setup aligned with the underlying HttpContext |
HttpContext or IHttpContextAccessor from DI |
AddHttpContext(...) or AddHttpContextAccessor(...) |
Replaces hand-rolled accessor wiring |
Important note for custom-claim tests:
- role-only helpers and custom-claim helpers are not identical because
FastMoq.Webadds compatibility identity claims by default - if the test is asserting exact
ClaimTypes.Name, email, or related identity values, passIncludeDefaultIdentityClaims = false - if your suite already has local wrappers for these helpers, re-point those wrappers to
FastMoq.Webfirst and simplify call sites later
Accessor And Middleware Testing
The same helper surface works for middleware and IHttpContextAccessor-driven services.
using FastMoq.Web.Extensions;
using Microsoft.AspNetCore.Http;
public class RequestContextReaderTests : MockerTestBase<RequestContextReader>
{
protected override Action<Mocker>? SetupMocksAction => mocker =>
{
var httpContext = mocker
.CreateHttpContext("Admin")
.SetRequestHeader("X-Correlation-Id", "corr-456")
.SetQueryParameter("tenant", "contoso");
mocker.AddHttpContextAccessor(httpContext);
};
[Fact]
public void Read_ShouldReturnHeaderAndQueryValues()
{
var result = Component.Read();
result.CorrelationId.Should().Be("corr-456");
result.Tenant.Should().Be("contoso");
}
}
Use this pattern when the system under test reads directly from IHttpContextAccessor, middleware InvokeAsync(HttpContext), or request headers/query collections without needing MVC controller infrastructure.
Keyed Services And Same-Type Dependencies
When the constructor under test takes the same interface more than once and production distinguishes those parameters with DI service keys, a single unkeyed GetOrCreateMock<T>() or GetMock<T>() collapses those dependencies into one double.
That is fine for ordinary tests that only need "some IBlobRepository", but it is too weak for tests where public vs private, primary vs secondary, or similar selection is part of the behavior.
Use separate doubles when:
- the constructor uses keyed DI attributes such as
FromKeyedServices - swapping the dependencies would be a user-visible bug
- the test is asserting routing or repository-selection behavior
Use keyed tracked mocks when the dependency is still conceptually a mock:
var publicRepo = Mocks.GetOrCreateMock<IBlobRepository>(new MockRequestOptions
{
ServiceKey = "public",
});
var privateRepo = Mocks.GetOrCreateMock<IBlobRepository>(new MockRequestOptions
{
ServiceKey = "private",
});
var controller = Mocks.CreateInstance<BlobAccessController>();
FastMoq keeps those tracked mocks separate. When the constructor uses [FromKeyedServices("public")] and [FromKeyedServices("private")], CreateInstance(...) and MockerTestBase<TComponent> resolve the matching keyed dependency instead of collapsing them to one unkeyed instance.
Use AddKeyedType(...) and GetKeyedObject<T>() when a fake or fixed instance reads better than a mock:
var publicRepo = new FakeBlobRepository();
var privateRepo = new FakeBlobRepository();
Mocks.AddKeyedType<IBlobRepository>("public", publicRepo);
Mocks.AddKeyedType<IBlobRepository>("private", privateRepo);
var controller = Mocks.CreateInstance<BlobAccessController>();
var resolvedPublicRepo = Mocks.GetKeyedObject<IBlobRepository>("public");
If repo selection itself is the behavior under test, explicit constructor injection with two separate doubles is equally valid and is often the clearest test shape.
Choosing explicit doubles vs keyed FastMoq setup
Pick explicit constructor injection with two separate doubles when:
- the test is about controller or service logic, not DI wiring
- you want the two roles to be obvious in the arrange step
- manual construction makes the test shorter or easier to read
- you do not need the test to prove that keyed constructor metadata is honored
Pick keyed FastMoq setup when:
- you want to keep MockerTestBase<TComponent> or CreateInstance(...) as the construction path
- you want the test to mirror the production keyed DI contract closely
- the constructor metadata itself is part of what you are trying to protect
- the suite already uses FastMoq auto-construction heavily and keyed setup removes custom wiring noise
Practical default:
- use explicit separate doubles for most behavior-focused unit tests
- use keyed FastMoq setup for tests that should fail if the keyed constructor resolution changes
- if both concerns matter, keep most tests explicit and add one small keyed wiring-focused test
Ordinary unit tests can stay ordinary. The important rule is: if swapping the keyed dependencies would be a bug, the test should not collapse them into one double.
If the problem is not keyed DI but an AmbiguousImplementationException from multiple loaded implementations of the same interface, take the same explicit approach: use AddType(...) when the test means one concrete implementation, or step outside the parent tracked graph with CreateStandaloneFastMock<T>() or MockingProviderRegistry.Default.CreateMock<T>() when you really need a detached double.
Analyzer note:
FMOQ0015warns on unkeyedGetOrCreateMock<T>(),GetMock<T>(),GetRequiredMock<T>(), andAddType<T>(...)usage when the target type has multiple keyed constructor parameters of that same abstraction and the current file is not already using keyed setup.
MockModel Equality Semantics
GetMockModel<T>() is useful for inspection, but it is not an identity contract for distinct doubles.
Current runtime behavior is intentionally type-oriented:
MockModel.Equals(...)and the==/!=operators compare the mocked type name (Type.Name) case-insensitively.GetHashCode()comes from the mockedType.CompareTo(...)sorts byType.FullName.- tracked-versus-standalone status, service keys, and provider-native object identity are not part of that equality test.
Practical rule:
- use
MockModelfor display or loose type-level inspection - use
FastMock,Instance,NativeMock, or the service key you supplied when the question is whether two registrations are actually distinct
This matters most for same-type dependencies: two different doubles of the same abstraction can still compare equal at the MockModel level even when they represent different roles in the test.
Because equality uses the simple type name, do not use MockModel equality as a cross-namespace uniqueness check.
Extending Known Types
Use AddKnownType(...) when a framework-style type needs special handling that does not belong in the normal type map.
Custom registrations are scoped to the current Mocker instance. They do not mutate global process state.
AddKnownType(...) vs AddType(...)
These APIs are related, but they are not interchangeable.
They can look similar in the simplest case because both can end with "FastMoq returns this object." The important difference is where they plug in:
- AddType(...) changes the normal type map for one dependency.
- AddKnownType(...) extends FastMoq's special handling pipeline for a category of framework-heavy types.
Use AddType(...) when
- You want to replace FastMoq's normal resolution for a type.
- You want a specific concrete implementation, factory result, or fixed instance.
- The problem is "what object should be returned for this abstraction?"
Mocks.AddType<IClock>(_ => new FakeClock(DateTimeOffset.Parse("2026-04-01T12:00:00Z")));
Another AddType(...) example that should stay on the normal type-map path:
Mocks.AddType<IPaymentGateway, FakePaymentGateway>(_ => new FakePaymentGateway("test-terminal"));
These are AddType(...) examples because:
IClockandIPaymentGatewayare ordinary application dependencies.- You are swapping one dependency for a specific implementation.
- There is no special FastMoq framework lifecycle or post-processing involved.
When AddKnownType(...) is the right tool
- The type is framework-heavy and needs special creation or post-processing behavior.
- The problem is not only "what object should be returned?" but also "how should FastMoq handle this type whenever it is resolved?"
- You want to plug into direct-instance resolution, managed-instance resolution, mock configuration, or object defaults.
Mocks.AddKnownType<IFileSystem>(
directInstanceFactory: (_, _) => new MockFileSystem().FileSystem,
includeDerivedTypes: true);
Examples that are specific to AddKnownType(...) and do not fit ordinary AddType(...) usage:
Mocks.AddKnownType<IHttpContextAccessor>(
applyObjectDefaults: (_, accessor) =>
{
accessor.HttpContext!.TraceIdentifier = "integration-test";
},
includeDerivedTypes: true);
Mocks.AddKnownType<HttpContext>(
configureMock: (_, _, fastMock) =>
{
var moqMock = fastMock.AsMoq();
moqMock.Setup(x => x.TraceIdentifier).Returns("trace-123");
});
These are AddKnownType(...) examples because:
- They are not just returning an object.
- They modify mock setup or post-creation defaults inside FastMoq's known-type pipeline.
- They apply to framework-style types where FastMoq already has built-in behavior.
Quick decision rule
If you are overriding one dependency in a test, use AddType(...).
If you are extending FastMoq's built-in handling for a framework-style type such as IFileSystem, HttpClient, DbContext, or HttpContext, use AddKnownType(...).
If the two APIs still look similar, ask this question:
- "Am I replacing one dependency?" -> AddType(...)
- "Am I teaching FastMoq how to treat this kind of framework-heavy type?" -> AddKnownType(...)
Analyzer note:
FMOQ0014warns on the context-aware compatibilityAddType(...)overloads and points them back toward AddKnownType(...).
Example: override a built-in direct instance
var customFileSystem = new MockFileSystem().FileSystem;
Mocks.AddKnownType<IFileSystem>(
directInstanceFactory: (_, _) => customFileSystem);
Example: apply custom post-processing
Mocks.AddKnownType<IHttpContextAccessor>(
applyObjectDefaults: (_, accessor) =>
{
accessor.HttpContext!.TraceIdentifier = "integration-test";
},
includeDerivedTypes: true);
When to prefer AddKnownType(...)
- The type is framework-heavy and has special lifecycle or initialization requirements.
- The behavior should apply whenever the type is resolved, not only when one constructor path is used.
- You want to extend or override built-in known-type handling without replacing the whole resolution pipeline.
Provider Notes
FastMoq is moving toward a provider-based architecture. The stable guidance for test authors is:
- Use FastMoq's portable APIs for creation, injection, and common helpers.
- Use the provider-native object only when you genuinely need library-specific arrangement behavior.
- Assume Moq compatibility is currently strongest, but new extension points should avoid hard-coding Moq assumptions unless the scenario is explicitly Moq-only.
ScenarioBuilder still works with each registered provider because it only orchestrates arrange, act, and assert steps and forwards provider-first verification through Mocker.Verify(...). The provider-specific part is still the arrangement code you put inside With(...) or When(...).
VerifyLogged(...) now follows the same default expectation model as provider-first verification: if you do not specify a count, it means at least once. Use TimesSpec when you need Exactly, AtLeast, AtMost, or the zero-invocation aliases NeverCalled / Never() semantics for captured log entries.
If you need provider-specific behavior for a tracked mock, prefer the typed provider-package extensions first, such as AsMoq() or AsNSubstitute().
Use GetNativeMock(...) or MockModel.NativeMock only when you truly need the raw provider object beyond those typed helpers.
You can also retrieve the provider-first abstraction directly:
var fastMock = Mocks.GetOrCreateMock<IOrderRepository>();
var moqMock = fastMock.AsMoq();
For NSubstitute-backed tests:
var fastMock = Mocks.GetOrCreateMock<IOrderRepository>();
fastMock.AsNSubstitute().Load(123).Returns(order);
Strict vs Presets
Strict is now best understood as a compatibility alias for MockFeatures.FailOnUnconfigured.
That means:
Mocks.Strict = true;
turns on fail-on-unconfigured behavior, but it does not replace the rest of the current Behavior flags.
It also still influences some compatibility-era fallback rules, such as whether FastMoq falls back to non-public constructors or methods when public resolution fails.
If you want to switch the whole behavior profile, use the explicit UseStrictPreset() and UseLenientPreset() helpers instead:
Mocks.UseStrictPreset();
Mocks.UseLenientPreset();
Use the preset helpers when you want a complete behavior profile. Use Strict only when you mean the fail-on-unconfigured compatibility behavior.
Breaking-change note:
- In
3.0.0,Strictwas often treated as a broader all-in-one switch. - In the current v4 release line, UseStrictPreset() is the explicit way to request the broader strict profile.
Strictremains available, but it should now be read as the narrower compatibility path rather than the full profile selector.
Separate compatibility note for known types:
- strict tracked
IFileSystemmocks can still expose built-in members such asDirectory - strict
HttpClientdoes not use the same breaking path DbContextis not part of thatIFileSystembreak
Executable Examples In This Repo
The best current examples are in FastMoq.TestingExample.
Start there if you want repo-backed samples for:
- multi-dependency service workflows
- built-in
IFileSystembehavior - logger verification
- fluent
Scenariousage with parameterless arrange/act/assert overloads - provider-first verification with TimesSpec.Once, TimesSpec.NeverCalled, TimesSpec.Exactly(...), TimesSpec.AtLeast(...), and TimesSpec.AtMost(...)
See Executable Testing Examples.
Recommended Test Flow
For most tests in this repo, this order is the least surprising:
- Register explicit type overrides with AddType(...) only when needed.
- Configure default mocks with GetOrCreateMock<T>().
- Use known-type helpers for
DbContext,HttpClient,IFileSystem, and web abstractions. - Create the component through MockerTestBase<TComponent> or CreateInstance(...).
- Assert behavior and verify the dependency interactions you actually care about.
Pitfalls to Avoid
- Do not use AddType(...) as a general replacement for GetOrCreateMock<T>().
- Do not collapse two keyed constructor dependencies into one unkeyed mock or registration when selection between them is part of the behavior under test.
- Do not bypass GetMockDbContext<TContext>() unless FastMoq's EF Core support is the thing you are explicitly testing around.
- Do not assume
CreateInstanceByType(...)alone is the best API for new code. UseInstanceCreationFlagswhen you need to express constructor-selection intent explicitly. - Do not make known-type extensions global. Keep them scoped to the
Mockerused by the test.