From 1d55b157716b75dc60a7bc9c01ea18ad5d55b293 Mon Sep 17 00:00:00 2001 From: Ian Johnson Date: Sun, 9 Aug 2026 16:59:54 -0400 Subject: [PATCH] Cover Moq and FakeItEasy in the testing docs The mocking packages shipped in rc9210 but the guide still read as though NSubstitute were the only option. The alternatives were a table inside a tip box, while every example on the page was written in NSubstitute syntax -- so a Moq reader hit code that does not work for them and no correction anywhere. Choosing a library is now its own section, with the same two configuration lines shown in all three through a code group. The Mock.Get step is called out, since it is the one real difference: NSubstitute and FakeItEasy inject the mock itself, while Moq injects Mock.Object and keeps the mock separate. The install block in the testing guide no longer names NSubstitute as though it were required alongside the xUnit package, and the NuGet nav lists the three mocking packages, which shipped without ever being linked. Examples are taken from the Moq and FakeItEasy integration tests, so the syntax is known to compile. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01FasPSEiCXbFwobHLur2pnv --- website/.vitepress/config.ts | 9 +++++++ website/guide/testing-mocks.md | 47 ++++++++++++++++++++++++++-------- website/guide/testing.md | 7 +++-- 3 files changed, 51 insertions(+), 12 deletions(-) diff --git a/website/.vitepress/config.ts b/website/.vitepress/config.ts index 32ead44..be37d5e 100644 --- a/website/.vitepress/config.ts +++ b/website/.vitepress/config.ts @@ -51,6 +51,15 @@ export default defineConfig({ link: 'https://www.nuget.org/packages/DependencyModules.Conventions/', }, { text: 'xUnit', link: 'https://www.nuget.org/packages/DependencyModules.xUnit/' }, + { + text: 'NSubstitute', + link: 'https://www.nuget.org/packages/DependencyModules.NSubstitute/', + }, + { text: 'Moq', link: 'https://www.nuget.org/packages/DependencyModules.Moq/' }, + { + text: 'FakeItEasy', + link: 'https://www.nuget.org/packages/DependencyModules.FakeItEasy/', + }, ], }, ], diff --git a/website/guide/testing-mocks.md b/website/guide/testing-mocks.md index ce2ddc2..1304853 100644 --- a/website/guide/testing-mocks.md +++ b/website/guide/testing-mocks.md @@ -44,8 +44,10 @@ holding. You wire nothing together yourself. Note what stayed real: `SummaryProvider` was not mocked, so the call still travels `Weather` → `SummaryProvider` → `IAiSummaryProvider`. Only the leaf was swapped. -::: tip Register the mocking library once -`[Mock]` needs a mocking framework, supplied by a separate package. Pick the one you already use: +## Choosing a mocking library + +`[Mock]` does not depend on a particular mocking library. It defines a seam, and a small package +fills it — so use whichever library you already have: | Package | Attribute | |---|---| @@ -53,25 +55,50 @@ Note what stayed real: `SummaryProvider` was not mocked, so the call still trave | `DependencyModules.Moq` | `[MoqSupport]` | | `DependencyModules.FakeItEasy` | `[FakeItEasySupport]` | +Install one and apply its attribute. Like the module attributes it works at assembly, class or +method level, and assembly is usually right: + ```shell -dotnet add package DependencyModules.NSubstitute +dotnet add package DependencyModules.Moq ``` ```csharp -[assembly: NSubstituteSupport] +[assembly: MoqSupport] ``` -Without one, `[Mock]` fails with a message telling you so. Like the module attributes it works at -assembly, class or method level; assembly is almost always right. +Without one, `[Mock]` fails with a message telling you so. -With NSubstitute and FakeItEasy the injected instance is also what you configure. Moq separates the -two, so the container gets `Mock.Object` and you reach the mock with `Mock.Get(instance)`: +### The same test in each -```csharp -Mock.Get(summaryProvider).Setup(x => x.Summarize(It.IsAny())).Returns("mild"); +Only the configuration lines differ — `[Mock]`, the injection and the assertions are identical. The +example above is NSubstitute; here are the other two: + +::: code-group + +```csharp [NSubstitute] +temperatureProvider.GetTemperature().Returns(38); +aiSummaryProvider.GetSummary().Returns("Sunny"); +``` + +```csharp [Moq] +Mock.Get(temperatureProvider).Setup(x => x.GetTemperature()).Returns(38); +Mock.Get(aiSummaryProvider).Setup(x => x.GetSummary()).Returns("Sunny"); +``` + +```csharp [FakeItEasy] +A.CallTo(() => temperatureProvider.GetTemperature()).Returns(38); +A.CallTo(() => aiSummaryProvider.GetSummary()).Returns("Sunny"); ``` + ::: +The `Mock.Get` in the Moq version is the one difference worth knowing about. NSubstitute and +FakeItEasy hand you an object that *is* the mock, so the parameter the container injected is the +thing you configure. Moq keeps the two apart — the container receives `Mock.Object`, and +`Mock.Get(instance)` gets you back to the `Mock` to set expectations on. + +Unconfigured members return `default` rather than throwing, in all three. + ## When you want a real object, not a mock A mock is right when you intend to **assert on the interaction** — what was called, with which diff --git a/website/guide/testing.md b/website/guide/testing.md index 5d32e4f..03ac3bf 100644 --- a/website/guide/testing.md +++ b/website/guide/testing.md @@ -51,7 +51,6 @@ real modules: ```shell dotnet add package DependencyModules.xUnit -dotnet add package DependencyModules.NSubstitute ``` ```csharp @@ -89,9 +88,13 @@ every test needs in one file at the assembly level: using DependencyModules.NSubstitute; [assembly: ApplicationModule] -[assembly: NSubstituteSupport] +[assembly: NSubstituteSupport] // or [MoqSupport] / [FakeItEasySupport] ``` +`NSubstituteSupport` is what enables [`[Mock]`](/guide/testing-mocks), and it comes from a separate +package — one per mocking library, so use whichever you already have. See +[choosing a mocking library](/guide/testing-mocks#choosing-a-mocking-library). + Every test in the project now gets `ApplicationModule` without saying so: ```csharp