");
+ text = text.Replace("### ", "##### ");
+ text = text.Replace("## ", "#### ");
+ text = text.Replace("# ", "### ");
+ text= text.Replace("C###", "C#");
+
return text;
}
;
diff --git a/v2/Generator/all.csv b/v2/Generator/all.csv
index 003f38a36..7623fc5a3 100644
--- a/v2/Generator/all.csv
+++ b/v2/Generator/all.csv
@@ -253,3 +253,4 @@ Nr,Key,Source,Category
252,RSCG_idempotency, https://github.com/ignatandrei/RSCG_idempotency,Idempotency
253,FastCloner, https://github.com/lofcz/FastCloner/,Clone
254,ErrorOrX, https://github.com/ANcpLua/ErrorOrX,API
+255,KnockOff, https://github.com/NeatooDotNet/KnockOff,Tests
diff --git a/v2/RSCGExamplesData/GeneratorDataRec.json b/v2/RSCGExamplesData/GeneratorDataRec.json
index 62b7082ba..baa03019d 100644
--- a/v2/RSCGExamplesData/GeneratorDataRec.json
+++ b/v2/RSCGExamplesData/GeneratorDataRec.json
@@ -1540,5 +1540,11 @@
"Category": 15,
"dtStart": "2026-02-02T00:00:00",
"show": true
+ },
+ {
+ "ID": "KnockOff",
+ "Category": 13,
+ "dtStart": "2026-02-13T00:00:00",
+ "show": true
}
]
\ No newline at end of file
diff --git a/v2/book/examples/ErrorOrX.html b/v2/book/examples/ErrorOrX.html
index bf8047088..f0be402a8 100644
--- a/v2/book/examples/ErrorOrX.html
+++ b/v2/book/examples/ErrorOrX.html
@@ -4,11 +4,11 @@ RSCG nr 254 : ErrorOrX
Info
Nuget : https://www.nuget.org/packages/ErrorOrX/
-You can find more details at : https://github.com/ANcpLua/ErrorOrX/
+You can find more details at : https://github.com/ANcpLua/ErrorOrX
Author :Alexander Nachtmanns
-Source: https://github.com/ANcpLua/ErrorOrX/
+Source: https://github.com/ANcpLua/ErrorOrX
About
diff --git a/v2/book/examples/KnockOff.html b/v2/book/examples/KnockOff.html
new file mode 100644
index 000000000..a61844820
--- /dev/null
+++ b/v2/book/examples/KnockOff.html
@@ -0,0 +1,64 @@
+
+RSCG nr 255 : KnockOff
+
+Info
+Nuget : https://www.nuget.org/packages/KnockOff/
+
+You can find more details at : https://github.com/NeatooDotNet/KnockOff
+
+Author :Keith Voels
+
+Source: https://github.com/NeatooDotNet/KnockOff
+
+About
+
+Generating test stubs with mocking for interfaces
+
+
+ How to use
+
+
+ Add reference to the KnockOff in the csproj
+
+
+
+This was for me the starting code
+
+
+ I have coded the file IMyClock.cs
+
+
+
+
+
+ I have coded the file TestClock.cs
+
+
+
+ And here are the generated files
+
+
+ The file generated is QuickStartRepoStub.Base.g.cs
+
+
+
+
+ The file generated is QuickStartRepoStub.g.cs
+
+
+
+
+ You can download the code and this page as pdf from
+
+ https://ignatandrei.github.io/RSCG_Examples/v2/docs/KnockOff
+
+
+
+
+
+ You can see the whole list at
+
+ https://ignatandrei.github.io/RSCG_Examples/v2/docs/List-of-RSCG
+
+
+
diff --git a/v2/book/list.html b/v2/book/list.html
index 957e64006..71c549521 100644
--- a/v2/book/list.html
+++ b/v2/book/list.html
@@ -17,7 +17,7 @@
-This is the list of 254 RSCG with examples =>
+This is the list of 255 RSCG with examples =>
diff --git a/v2/book/pandocHTML.yaml b/v2/book/pandocHTML.yaml
index 0e2720b7a..be7df0edb 100644
--- a/v2/book/pandocHTML.yaml
+++ b/v2/book/pandocHTML.yaml
@@ -268,6 +268,7 @@ input-files:
- examples/RSCG_idempotency.html
- examples/FastCloner.html
- examples/ErrorOrX.html
+- examples/KnockOff.html
# or you may use input-file: with a single value
# defaults:
diff --git a/v2/rscg_examples/KnockOff/description.json b/v2/rscg_examples/KnockOff/description.json
new file mode 100644
index 000000000..fa4db2ea3
--- /dev/null
+++ b/v2/rscg_examples/KnockOff/description.json
@@ -0,0 +1,22 @@
+{
+ "generator":{
+ "name":"KnockOff",
+ "nuget":[
+ "https://www.nuget.org/packages/KnockOff/"
+ ],
+ "link":"https://github.com/NeatooDotNet/KnockOff",
+ "author":"Keith Voels",
+ "source":"https://github.com/NeatooDotNet/KnockOff"
+ },
+ "data":{
+ "goodFor":["Generating test stubs with mocking for interfaces"],
+ "csprojDemo":"TestClock.csproj",
+ "csFiles":["IMyClock.cs","TestClock.cs"],
+ "excludeDirectoryGenerated":[""],
+ "includeAdditionalFiles":[""]
+ },
+ "links":{
+ "blog":"",
+ "video":""
+ }
+}
\ No newline at end of file
diff --git a/v2/rscg_examples/KnockOff/nuget.txt b/v2/rscg_examples/KnockOff/nuget.txt
new file mode 100644
index 000000000..12db84572
--- /dev/null
+++ b/v2/rscg_examples/KnockOff/nuget.txt
@@ -0,0 +1 @@
+A Roslyn Source Generator for creating unit test stubs. Unlike Moq's fluent runtime configuration, KnockOff uses partial classes for compile-time setup—trading flexibility for readability and performance.
\ No newline at end of file
diff --git a/v2/rscg_examples/KnockOff/readme.txt b/v2/rscg_examples/KnockOff/readme.txt
new file mode 100644
index 000000000..acc677248
--- /dev/null
+++ b/v2/rscg_examples/KnockOff/readme.txt
@@ -0,0 +1,529 @@
+# KnockOff
+
+A .NET mocking library that lets you define reusable stub classes — with full mocking capabilities built in.
+
+Define your test double once. Reuse it across your test project. Customize it per-test with Return, Call, Verify, and When chains. No more copying mock setups between tests or maintaining shared factory methods full of `Arg.Any<>()`.
+
+Powered by Roslyn source generation for [tighter type safety](docs/type-safety.md) — more issues surface as compile errors instead of runtime surprises.
+
+Claude Code was used to write this library. Skip to more [AI discussion](#ai).
+
+[](https://www.nuget.org/packages/KnockOff/)
+[](https://github.com/NeatooDotNet/KnockOff/actions)
+[](https://opensource.org/licenses/MIT)
+
+
+## KnockOff Stub
+
+There are [9 patterns](docs/guides/stub-patterns.md) total, including a [standard fluent mocking approach](docs/guides/stub-patterns.md#inline-interface-pattern) with inline stubs. But reusable stub classes are where KnockOff stands apart:
+
+
+```cs
+[KnockOff]
+public partial class MyRepoStub(List Users) : IMyRepo
+{
+ protected override User? GetUser_(int id)
+ {
+ return Users.Single(u => u.Id == id);
+ }
+
+ protected override void Update_(User user)
+ {
+ Assert.Contains(user, Users);
+ }
+}
+```
+
+
+- **`[KnockOff]` + `partial class`** — KnockOff generates a base class that implements every member of `IMyRepo`. Your stub is a real class — define it once, reuse it across your entire test project. Pass it around, register it in DI, share it between test fixtures.
+- **Constructor parameters** — `List Users` is a primary constructor. Test data flows in naturally, just like any other C# class.
+- **Overrides are optional** — `GetUser_` and `Update_` override the generated defaults. Only override what you need — everything else still works with [Return/Call](docs/guides/methods.md), [Return(value)](docs/reference/interceptor-api.md), or [When chains](docs/guides/parameter-matching.md).
+- **Tighter type safety** — Every Return, Call, and When call is complete in a single step — no forgotten `.Returns()` that [silently breaks at runtime](docs/type-safety.md). No manual `` type parameters that can drift. [Details →](docs/type-safety.md)
+
+This stub is also a full mock. It has [Verify](docs/guides/verification.md), [Strict mode](docs/guides/strict-mode.md), [Async](docs/guides/async-patterns.md), and [Source Delegation](docs/guides/source-delegation.md) — all on the same reusable class.
+
+
+## Why I Wrote KnockOff
+
+I often wanted to reuse my mocks.
+Especially in my integration test library where I may even register my mocks.
+I found myself either copying my mock definitions code or creating shared methods like this:
+
+**NSubstitute:**
+
+```cs
+public static IMyRepo NSubstituteMock(List users)
+{
+ var myRepoMock = Substitute.For();
+
+ // Setup: configure GetUser to look up from the list based on id
+ myRepoMock.GetUser(Arg.Any())
+ .Returns(callInfo => users.SingleOrDefault(u => u.Id == callInfo.Arg()));
+
+ // Setup: configure Update to assert user exists in list
+ myRepoMock.When(x => x.Update(Arg.Any()))
+ .Do(callInfo => Assert.Contains(callInfo.Arg(), users));
+
+ return myRepoMock;
+}
+```
+
+
+Here's another [example from PowerToys](https://github.com/microsoft/PowerToys/blob/main/src/settings-ui/Settings.UI.UnitTests/Mocks/ISettingsUtilsMocks.cs).
+
+But I find that hard to read and unintuitive. Also, my shared methods accumulated extra parameters for variations across different tests.
+
+
+## So I Created KnockOff
+
+You can create a stub to implement [interfaces](docs/guides/stub-patterns.md) or non-sealed [classes](docs/guides/stub-patterns.md) with virtual methods.
+Yet, you can still customize the stub per test.
+All while having the features you would expect with a full mocking library.
+
+With the stub above, your tests are:
+
+
+```cs
+var myRepoKO = new MyRepoStub([new User { Id = 1 }, new User { Id = 2 }]);
+var userDomainModel = new UserDomainModel(myRepoKO);
+
+Assert.True(userDomainModel.Fetch(1));
+
+// I have Verify on my Stub!
+myRepoKO.GetUser.Verify(Called.Once);
+```
+
+
+Need different behavior for a specific test? Override with Return/Call:
+
+
+```cs
+var user1 = new User { Id = 1 }; // Ignored do to per-test configuration
+var myRepoKO = new MyRepoStub([user1]);
+var userDomainModel = new UserDomainModel(myRepoKO);
+
+var user2 = new User { Id = 2 };
+
+// When and Return overrides the stub methods
+myRepoKO.GetUser.When(2).Return(user2).Verifiable();
+myRepoKO.Update.Call(u => Assert.Same(u, user2)).Verifiable();
+
+userDomainModel.Fetch(2);
+userDomainModel.Update();
+
+myRepoKO.Verify();
+```
+
+
+**Now I have my stubs and mocks in one!**
+
+---
+
+## What Sets KnockOff Apart
+
+- **[Reusable stub classes](docs/guides/reusable-stubs.md)** — Define once, customize per-test. Your stub is a real class — pass it through constructors, register it in DI.
+- **[Source delegation](docs/guides/source-delegation.md)** — Delegate to a real implementation, override only specific methods. No equivalent in Moq or NSubstitute.
+- **[Protected methods](docs/guides/protected-methods.md)** — Same `Return`/`Call`/`Verify` API, fully typed. No string-based names, no manual subclasses.
+- **[Ref/out parameters](docs/guides/ref-out-parameters.md)** — Natural lambda syntax with `ref`/`out` keywords. No special matchers or index-based access.
+- **[Multiple interfaces](docs/guides/multiple-interfaces.md)** — Unified interceptors on one stub. No `.As()` references or casting.
+- **[Tighter type safety](docs/type-safety.md)** — Each Return/Call/When call is complete in one step — no forgotten `.Returns()` that silently breaks at runtime.
+- **[Parameter matching](docs/guides/parameter-matching-comparison.md)** — `Return((a, b) => a > 0 ? 100 : 0)` — standard C# conditionals instead of `Arg.Is<>` or `It.Is<>` per parameter.
+- **Built-in argument capture** — `LastArg`, `LastArgs`, `LastSetValue`, `LastSetEntry` — no manual `Arg.Do<>` or `Callback<>` setup.
+- **Event verification** — `VerifyAdd()` / `VerifyRemove()` / `HasSubscribers` — not available in Moq or NSubstitute.
+- **Explicit Get/Set verification** — `VerifyGet(Called)` / `VerifySet(Called)` for properties and indexers.
+- **Stubbing concrete classes** — Override virtual methods on non-sealed classes with the same API.
+
+---
+
+## Quick Start
+
+### Install
+
+```bash
+dotnet add package KnockOff
+```
+
+### Create a Stub
+
+
+```cs
+public interface IQuickStartRepo
+{
+ User? GetUser(int id);
+}
+
+[KnockOff]
+public partial class QuickStartRepoStub : IQuickStartRepo { }
+
+public class QuickStartCreateStubTests
+{
+ [Fact]
+ public void CreateStub_IsReady()
+ {
+ var stub = new QuickStartRepoStub();
+
+ IQuickStartRepo repository = stub;
+ Assert.NotNull(repository);
+ }
+}
+```
+
+
+### Configure and Verify
+
+
+```cs
+[Fact]
+public void ConfigureStub_WithReturn()
+{
+ var stub = new QuickStartRepoStub();
+
+ stub.GetUser.Return((id) => new User { Id = id, Name = "Test User" });
+
+ IQuickStartRepo repository = stub;
+ var user = repository.GetUser(42);
+
+ Assert.NotNull(user);
+ Assert.Equal(42, user.Id);
+ Assert.Equal("Test User", user.Name);
+}
+```
+
+
+
+```cs
+[Fact]
+public void VerifyCalls_WithVerifiable()
+{
+ var stub = new QuickStartRepoStub();
+ stub.GetUser.Return((id) => new User { Id = id, Name = "Test" }).Verifiable();
+
+ IQuickStartRepo repository = stub;
+
+ var user = repository.GetUser(42);
+
+ // Verify() checks all members marked with .Verifiable()
+ stub.Verify();
+}
+```
+
+
+---
+
+## The Difference
+
+**Moq:**
+```cs
+mock.Setup(x => x.GetUser(It.Is(id => id > 0)))
+ .Returns(id => new User { Id = id });
+```
+
+**NSubstitute:**
+
+```cs
+var repo = Substitute.For();
+repo.GetUser(Arg.Is(id => id > 0)).Returns(x => new User { Id = x.Arg() });
+```
+
+
+**KnockOff:**
+
+```cs
+var stub = new CompareUserRepoStub();
+stub.GetUser.Return((id) => id > 0 ? new User { Id = id } : null);
+```
+
+
+No `It.Is<>()`. No `Arg.Is<>()`. No `x.Arg()`. The parameter is just `id`.
+
+---
+
+For side-by-side comparison tables (methods, properties, events, delegates, indexers), see the [complete comparison guide](docs/comparison.md).
+
+---
+
+## Argument Matching
+
+**Moq:**
+```cs
+// Moq - It.Is per parameter
+mock.Setup(x => x.Add(It.Is(a => a > 0), It.IsAny())).Returns(100);
+```
+
+**NSubstitute:**
+
+```cs
+// NSubstitute - Arg.Is per parameter (permanent matchers)
+calc.Add(Arg.Is(a => a > 0), Arg.Any()).Returns(100);
+```
+
+
+**KnockOff:**
+
+```cs
+// KnockOff - Returns with conditional (permanent, matches all calls)
+stub.Add.Return((a, b) => a > 0 ? 100 : 0);
+```
+
+
+
+```cs
+// KnockOff - When() for sequential matching (first match returns 100, then falls through)
+stub.Add.When((a, b) => a > 0).Return(100).ThenCall((a, b) => a + b);
+```
+
+
+**Multiple specific values:**
+
+**Moq:**
+```cs
+mock.Setup(x => x.Add(1, 2)).Returns(100);
+mock.Setup(x => x.Add(3, 4)).Returns(200);
+```
+
+
+```cs
+// Multiple specific values
+calc.Add(1, 2).Returns(100);
+calc.Add(3, 4).Returns(200);
+```
+
+
+
+```cs
+stub.Add.When(1, 2).Return(100);
+stub.Add.When(3, 4).Return(200);
+```
+
+
+**Note:** Moq and NSubstitute matchers are permanent -- they match all qualifying calls. KnockOff's `When()` is sequential -- matchers are consumed in order. Use `Return(callback)` with conditionals for permanent matching behavior.
+
+### Argument Capture
+
+**Moq:**
+```cs
+// Moq - requires Callback setup
+int capturedA = 0, capturedB = 0;
+mock.Setup(x => x.Add(It.IsAny(), It.IsAny()))
+ .Callback((a, b) => { capturedA = a; capturedB = b; });
+mock.Object.Add(1, 2);
+```
+
+**NSubstitute:**
+
+```cs
+// NSubstitute - requires Arg.Do in setup
+int capturedA = 0, capturedB = 0;
+calc.Add(Arg.Do(x => capturedA = x), Arg.Do(x => capturedB = x));
+calc.Add(1, 2);
+```
+
+
+**KnockOff:**
+
+```cs
+// KnockOff - built-in, no pre-setup
+var tracking = stub.Add.Return((a, b) => a + b);
+ICalculator calc = stub;
+calc.Add(1, 2);
+var (a, b) = tracking.LastArgs; // Named tuple: a = 1, b = 2
+```
+
+
+For full comparisons of properties, events, delegates, and indexers, see the [complete comparison guide](docs/comparison.md).
+
+---
+
+## Method Overload Resolution
+
+**The Problem:** When an interface has overloaded methods with the same parameter count but different types:
+
+
+```cs
+public interface IFormatter
+{
+ string Format(string input, bool uppercase);
+ string Format(string input, int maxLength);
+}
+```
+
+
+### Any-Value Matching
+
+**Moq:**
+```cs
+// It.IsAny() required - compiler needs the types to resolve overload
+mock.Setup(x => x.Format(It.IsAny(), It.IsAny())).Returns("bool overload");
+mock.Setup(x => x.Format(It.IsAny(), It.IsAny())).Returns("int overload");
+```
+
+**NSubstitute:**
+
+```cs
+// Arg.Any() required - compiler needs the types to resolve overload
+formatter.Format(Arg.Any(), Arg.Any()).Returns("bool overload");
+formatter.Format(Arg.Any(), Arg.Any()).Returns("int overload");
+```
+
+
+**KnockOff:**
+
+```cs
+// Explicit parameter types resolve the overload - standard C# syntax
+stub.Format.Return((string input, bool uppercase) => "bool overload");
+stub.Format.Return((string input, int maxLength) => "int overload");
+```
+
+
+### Specific-Value Matching
+
+**NSubstitute:**
+
+```cs
+// Specific value matching - literals work when all args are specific
+formatter.Format("test", true).Returns("UPPERCASE");
+formatter.Format("test", 10).Returns("truncated");
+```
+
+
+**KnockOff:**
+
+```cs
+// Specific value matching - parameter types resolve the overload
+stub.Format.When("test", true).Return("UPPERCASE");
+stub.Format.When("test", 10).Return("truncated");
+```
+
+
+### Argument Access
+
+**Moq:**
+```cs
+// To use argument values, extract via Returns:
+mock.Setup(x => x.Format(It.IsAny(), It.IsAny()))
+ .Returns((input, uppercase) => uppercase ? input.ToUpper() : input);
+```
+
+**NSubstitute:**
+
+```cs
+// To use argument values, extract from CallInfo:
+formatter.Format(Arg.Any(), Arg.Any())
+ .Returns(x => x.ArgAt(1) ? x.ArgAt(0).ToUpper() : x.ArgAt(0));
+```
+
+
+**KnockOff:**
+
+```cs
+// Arguments are directly available with names and types:
+stub.Format.Return((string input, bool uppercase) => uppercase ? input.ToUpper() : input);
+```
+
+
+**The Difference:**
+- Moq: `It.IsAny()` + `.Returns((input, uppercase) => ...)` to match any value and access arguments
+- NSubstitute: `Arg.Any()` + `x.ArgAt(1)` to match any value and access arguments
+- KnockOff: `(string input, bool uppercase)` - standard C# lambda with named, typed parameters
+
+---
+
+## Three Stub Patterns
+
+KnockOff supports [9 patterns](docs/guides/stub-patterns.md) total. Here are the three most common:
+
+**[Standalone](docs/guides/stub-patterns.md#standalone-pattern)** - Reusable across your project:
+
+```cs
+[KnockOff]
+public partial class ReadmeStandaloneStub : IUserRepo { }
+```
+
+
+**[Inline Interface](docs/guides/stub-patterns.md#inline-interface-pattern)** - Test-local stubs:
+
+```cs
+[Fact]
+public void InlineInterface_Pattern()
+{
+ var stub = new Stubs.IUserRepo();
+ stub.GetUser.Return((id) => new User { Id = id });
+
+ IUserRepo repo = stub;
+ Assert.NotNull(repo.GetUser(1));
+}
+```
+
+
+**[Inline Class](docs/guides/stub-patterns.md#inline-class-pattern)** - Stub virtual members:
+
+```cs
+[Fact]
+public void InlineClass_Pattern()
+{
+ var stub = new Stubs.MyService();
+ stub.GetUser.Return((id) => new User { Id = id });
+
+ MyService service = stub.Object;
+ Assert.NotNull(service.GetUser(1));
+}
+```
+
+
+---
+
+## Roslyn Source Generation
+
+KnockOff uses Roslyn source generation, which means:
+
+- No more `Arg.Any<>()`. No more `It.IsAny<>()`. Just write C#
+- If the method signature changes you get a compile error
+- There's a small performance gain but honestly it's negligible
+
+Source generation opens doors beyond traditional mocking — I've already added [9 patterns](docs/guides/stub-patterns.md) and features like [Source Delegation](docs/guides/source-delegation.md), with more ideas to come.
+
+**What other ideas do you have?** Open a [discussion](https://github.com/NeatooDotNet/KnockOff/discussions).
+
+
+## AI
+
+This is an idea I've had for years but never took the time to implement. With my ideas and guidance, Claude Code has written the entirety of this library — the Roslyn source generator, the runtime library, the tests, and the documentation.
+
+Source generation turned out to be a great fit for AI code generation. The work is highly patterned: analyze an interface, generate code for each member, handle edge cases across 9 patterns and 4 member types. That's exactly the kind of systematic, repetitive-but-varied work where AI excels. I designed the API and patterns; Claude Code implemented them across every combination.
+
+### Claude Code Skill
+
+KnockOff includes a [Claude Code skill](skills/knockoff/) that teaches Claude how to use the library. Copy the `skills/knockoff/` directory into your project and Claude Code will know how to create stubs, configure behavior, write tests with KnockOff, and migrate from Moq — without you explaining the API.
+
+The skill includes slash commands:
+- **`/knockoff:create-stub`** — Create a new stub class with the pattern of your choice
+- **`/knockoff:migrate-from-moq`** — Convert existing Moq tests to KnockOff
+- **`/knockoff:troubleshoot`** — Diagnose and fix common KnockOff issues
+
+---
+
+## Documentation
+
+- **[Getting Started](docs/getting-started.md)** - Installation and first stub
+- **[Stub Patterns](docs/guides/stub-patterns.md)** - Standalone, inline interface, inline class
+- **[Interceptor API](docs/reference/interceptor-api.md)** - Complete `Returns`, `Execute`, `Get`, `Set` reference
+- **[Source Delegation](docs/guides/source-delegation.md)** - Delegate to real implementations
+- **[Full Comparison Guide](docs/comparison.md)** - Properties, events, delegates, indexers vs Moq and NSubstitute
+- **[Migration from Moq](docs/migration/from-moq.md)** - Step-by-step migration guide
+- **[Migration from NSubstitute](docs/migration/from-nsubstitute.md)** - Comparison and migration guide
+
+---
+
+## License
+
+MIT License. See [LICENSE](LICENSE) for details.
+
+---
+
+## Contributing
+
+Contributions welcome! See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
+
+- **Issues**: [GitHub Issues](https://github.com/NeatooDotNet/KnockOff/issues)
+- **Pull Requests**: Bug fixes, features, documentation
+- **Discussions**: [GitHub Discussions](https://github.com/NeatooDotNet/KnockOff/discussions)
diff --git a/v2/rscg_examples/KnockOff/src/Mock/IMyClock.cs b/v2/rscg_examples/KnockOff/src/Mock/IMyClock.cs
new file mode 100644
index 000000000..b5f9f222d
--- /dev/null
+++ b/v2/rscg_examples/KnockOff/src/Mock/IMyClock.cs
@@ -0,0 +1,7 @@
+namespace MockData;
+
+public interface IMyClock
+{
+ public DateTime GetNow();
+ public DateTime GetUtcNow();
+}
\ No newline at end of file
diff --git a/v2/rscg_examples/KnockOff/src/Mock/MockData.csproj b/v2/rscg_examples/KnockOff/src/Mock/MockData.csproj
new file mode 100644
index 000000000..9ed914b5b
--- /dev/null
+++ b/v2/rscg_examples/KnockOff/src/Mock/MockData.csproj
@@ -0,0 +1,9 @@
+
+
+
+ net10.0
+ enable
+ enable
+
+
+
diff --git a/v2/rscg_examples/KnockOff/src/MockRock.slnx b/v2/rscg_examples/KnockOff/src/MockRock.slnx
new file mode 100644
index 000000000..e2f5c864b
--- /dev/null
+++ b/v2/rscg_examples/KnockOff/src/MockRock.slnx
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/v2/rscg_examples/KnockOff/src/TestClock/TestClock.cs b/v2/rscg_examples/KnockOff/src/TestClock/TestClock.cs
new file mode 100644
index 000000000..ae733f450
--- /dev/null
+++ b/v2/rscg_examples/KnockOff/src/TestClock/TestClock.cs
@@ -0,0 +1,26 @@
+
+using KnockOff;
+
+namespace TestClock;
+
+[KnockOff]
+public partial class QuickStartRepoStub : IMyClock { }
+
+
+[TestClass]
+public class TestClock
+{
+ [TestMethod]
+ public void TestMyClock()
+ {
+ var expectations = new QuickStartRepoStub();
+ expectations.GetNow.Return(DateTime.Now.AddYears(-1));
+
+ IMyClock mock = expectations;
+ var data= mock.GetNow();
+ Assert.AreEqual(DateTime.Now.Year -1, data.Year);
+ expectations.Verify();
+ }
+}
+
+
diff --git a/v2/rscg_examples/KnockOff/src/TestClock/TestClock.csproj b/v2/rscg_examples/KnockOff/src/TestClock/TestClock.csproj
new file mode 100644
index 000000000..69ea89824
--- /dev/null
+++ b/v2/rscg_examples/KnockOff/src/TestClock/TestClock.csproj
@@ -0,0 +1,28 @@
+
+
+
+ net10.0
+ enable
+ enable
+
+ false
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+ $(BaseIntermediateOutputPath)\GX
+
+
+
diff --git a/v2/rscg_examples/KnockOff/src/TestClock/Usings.cs b/v2/rscg_examples/KnockOff/src/TestClock/Usings.cs
new file mode 100644
index 000000000..f3be1f99c
--- /dev/null
+++ b/v2/rscg_examples/KnockOff/src/TestClock/Usings.cs
@@ -0,0 +1,2 @@
+global using Microsoft.VisualStudio.TestTools.UnitTesting;
+global using MockData;
diff --git a/v2/rscg_examples/KnockOff/video.json b/v2/rscg_examples/KnockOff/video.json
new file mode 100644
index 000000000..ebcf74453
--- /dev/null
+++ b/v2/rscg_examples/KnockOff/video.json
@@ -0,0 +1,39 @@
+{
+ "scriptName": "KnockOff",
+ "steps":
+[
+ {"typeStep":"exec","arg":"clipchamp.exe launch"},
+ {"typeStep":"text","arg": "Welcome to Roslyn Examples"},
+ {"typeStep":"text","arg":"If you want to see more examples , see List Of RSCG"},
+ {"typeStep":"browser","arg":"https://ignatandrei.github.io/RSCG_Examples/v2/docs/List-of-RSCG"},
+ {"typeStep":"text","arg": "My name is Andrei Ignat and I am deeply fond of Roslyn Source Code Generator. "},
+
+{"typeStep":"text","arg": "Today I will present KnockOff . Generating test stubs with mocking for interfaces ."},
+{"typeStep":"browser","arg":"https://www.nuget.org/packages/KnockOff/"},
+{"typeStep":"text","arg": "The whole example is here"},
+{"typeStep":"browser","arg":"https://ignatandrei.github.io/RSCG_Examples/v2/docs/KnockOff"},
+{"typeStep":"text","arg": "You can download the code from here"},
+{"typeStep":"browser","arg":"https://ignatandrei.github.io/RSCG_Examples/v2/docs/KnockOff#download-example-net--c-"},
+{"typeStep":"text","arg":"Here is the code downloaded "},
+{"typeStep":"exec","arg":"explorer.exe /select,D:\\gth\\RSCG_Examples\\v2\\Generator.sln"},
+{"typeStep":"text","arg": "So , let's start the project with Visual Studio Code "},
+{"typeStep":"stepvscode","arg": "-n D:\\gth\\RSCG_Examples\\v2"},
+
+{"typeStep":"text","arg": "To use it ,you will put the Nuget KnockOff into the csproj "},
+
+{"typeStep":"stepvscode","arg": "-r -g D:\\gth\\RSCG_Examples\\v2\\rscg_examples\\KnockOff\\src\\TestClock\\TestClock.csproj"},
+
+{"typeStep":"text","arg": "And now I will show you an example of using KnockOff"},
+
+{"typeStep":"hide","arg": "now execute the tour in VSCode"},
+{"typeStep":"tour", "arg": "src/.tours/"},
+{"typeStep":"text","arg":" And I will execute the project"},
+{"typeStep":"showproj", "arg":"TestClock.csproj"},
+{"typeStep":"text","arg":" This concludes the project"},
+{"typeStep":"waitseconds","arg":"30"},
+{"typeStep":"text","arg": "Remember, you can download the code from here"},
+{"typeStep":"browser","arg":"https://ignatandrei.github.io/RSCG_Examples/v2/docs/KnockOff#download-example-net--c-",
+SpeakTest=" "},
+{"typeStep":"waitseconds","arg":"30"},
+]
+}
diff --git a/v2/rscg_examples_site/docs/Authors/Keith_Voels.md b/v2/rscg_examples_site/docs/Authors/Keith_Voels.md
new file mode 100644
index 000000000..7f2b1ac1c
--- /dev/null
+++ b/v2/rscg_examples_site/docs/Authors/Keith_Voels.md
@@ -0,0 +1,7 @@
+# Author : Keith Voels
+
+Number RSCG: 1
+
+
+ 1 [KnockOff](/docs/KnockOff) [](https://www.nuget.org/packages/KnockOff/)  2026-02-13
+
diff --git a/v2/rscg_examples_site/docs/Categories/Tests.md b/v2/rscg_examples_site/docs/Categories/Tests.md
index 11c4931b8..211778688 100644
--- a/v2/rscg_examples_site/docs/Categories/Tests.md
+++ b/v2/rscg_examples_site/docs/Categories/Tests.md
@@ -1,18 +1,20 @@
Tests
-Number RSCG: 7
+Number RSCG: 8
1 [Imposter](/docs/Imposter) [](https://www.nuget.org/packages/Imposter/)  2025-12-13
- 2 [mocklis](/docs/mocklis) [](https://www.nuget.org/packages/mocklis/)  2024-01-03
+ 2 [KnockOff](/docs/KnockOff) [](https://www.nuget.org/packages/KnockOff/)  2026-02-13
- 3 [MockMe](/docs/MockMe) [](https://www.nuget.org/packages/MockMe/)  2025-02-10
+ 3 [mocklis](/docs/mocklis) [](https://www.nuget.org/packages/mocklis/)  2024-01-03
- 4 [MSTest](/docs/MSTest) [](https://www.nuget.org/packages/MSTest.SourceGeneration/)  2024-04-04
+ 4 [MockMe](/docs/MockMe) [](https://www.nuget.org/packages/MockMe/)  2025-02-10
- 5 [Ridge](/docs/Ridge) [](https://www.nuget.org/packages/Ridge/)  2023-08-20
+ 5 [MSTest](/docs/MSTest) [](https://www.nuget.org/packages/MSTest.SourceGeneration/)  2024-04-04
- 6 [Rocks](/docs/Rocks) [](https://www.nuget.org/packages/Rocks/)  2023-04-16
+ 6 [Ridge](/docs/Ridge) [](https://www.nuget.org/packages/Ridge/)  2023-08-20
- 7 [TUnit](/docs/TUnit) [](https://www.nuget.org/packages/TUnit/)  2025-11-08
+ 7 [Rocks](/docs/Rocks) [](https://www.nuget.org/packages/Rocks/)  2023-04-16
+
+ 8 [TUnit](/docs/TUnit) [](https://www.nuget.org/packages/TUnit/)  2025-11-08
\ No newline at end of file
diff --git a/v2/rscg_examples_site/docs/Categories/_PrimitiveTests.mdx b/v2/rscg_examples_site/docs/Categories/_PrimitiveTests.mdx
index 816acc669..7b4c6c54e 100644
--- a/v2/rscg_examples_site/docs/Categories/_PrimitiveTests.mdx
+++ b/v2/rscg_examples_site/docs/Categories/_PrimitiveTests.mdx
@@ -2,17 +2,19 @@
1 [Imposter](/docs/Imposter) [](https://www.nuget.org/packages/Imposter/)  2025-12-13
- 2 [mocklis](/docs/mocklis) [](https://www.nuget.org/packages/mocklis/)  2024-01-03
+ 2 [KnockOff](/docs/KnockOff) [](https://www.nuget.org/packages/KnockOff/)  2026-02-13
- 3 [MockMe](/docs/MockMe) [](https://www.nuget.org/packages/MockMe/)  2025-02-10
+ 3 [mocklis](/docs/mocklis) [](https://www.nuget.org/packages/mocklis/)  2024-01-03
- 4 [MSTest](/docs/MSTest) [](https://www.nuget.org/packages/MSTest.SourceGeneration/)  2024-04-04
+ 4 [MockMe](/docs/MockMe) [](https://www.nuget.org/packages/MockMe/)  2025-02-10
- 5 [Ridge](/docs/Ridge) [](https://www.nuget.org/packages/Ridge/)  2023-08-20
+ 5 [MSTest](/docs/MSTest) [](https://www.nuget.org/packages/MSTest.SourceGeneration/)  2024-04-04
- 6 [Rocks](/docs/Rocks) [](https://www.nuget.org/packages/Rocks/)  2023-04-16
+ 6 [Ridge](/docs/Ridge) [](https://www.nuget.org/packages/Ridge/)  2023-08-20
- 7 [TUnit](/docs/TUnit) [](https://www.nuget.org/packages/TUnit/)  2025-11-08
+ 7 [Rocks](/docs/Rocks) [](https://www.nuget.org/packages/Rocks/)  2023-04-16
+
+ 8 [TUnit](/docs/TUnit) [](https://www.nuget.org/packages/TUnit/)  2025-11-08
### See category
diff --git a/v2/rscg_examples_site/docs/RSCG-Examples/ErrorOrX.md b/v2/rscg_examples_site/docs/RSCG-Examples/ErrorOrX.md
index c465edcd2..d478fb6c1 100644
--- a/v2/rscg_examples_site/docs/RSCG-Examples/ErrorOrX.md
+++ b/v2/rscg_examples_site/docs/RSCG-Examples/ErrorOrX.md
@@ -46,10 +46,10 @@ Alexander Nachtmanns

:::
-### Original Readme
+## Original Readme
:::note
-# ErrorOrX
+### ErrorOrX
[](https://www.nuget.org/packages/ErrorOrX.Generators/)
[](https://www.nuget.org/packages/ErrorOrX.Generators/)
@@ -58,7 +58,7 @@ Alexander Nachtmanns
Railway-Oriented Programming for .NET with source-generated ASP.NET Core Minimal API integration. Zero boilerplate, full
Native AOT support.
-## Features
+###### Features
- **Discriminated Unions** - `ErrorOr` represents success or a list of typed errors
- **Fluent API** - Chain operations with `Then`, `Else`, `Match`, `Switch`, and `FailIf`
@@ -72,12 +72,12 @@ Native AOT support.
- **API Versioning** - Integrates with Asp.Versioning.Http for versioned endpoint groups
- **41 Analyzers** - Real-time IDE feedback for route conflicts, binding errors, AOT compatibility
-## What the Generator Produces
+###### What the Generator Produces
The source generator transforms your handler methods into complete ASP.NET Core Minimal API endpoints.
You write the business logic, the generator handles everything else.
-### Endpoint Wiring
+######### Endpoint Wiring
For each `[Get]`, `[Post]`, `[Put]`, `[Delete]`, `[Patch]` method:
@@ -87,7 +87,7 @@ For each `[Get]`, `[Post]`, `[Put]`, `[Delete]`, `[Patch]` method:
[See EndpointMetadataEmitter.cs](https://github.com/ANcpLua/ErrorOrX/src/ErrorOrX.Generators/Emitters/EndpointMetadataEmitter.cs)
-### Parameter Binding
+######### Parameter Binding
Automatic inference based on type and HTTP method:
@@ -101,7 +101,7 @@ Automatic inference based on type and HTTP method:
[See BindingCodeEmitter.cs](https://github.com/ANcpLua/ErrorOrX/src/ErrorOrX.Generators/Emitters/BindingCodeEmitter.cs)
-### Error-to-HTTP Mapping
+######### Error-to-HTTP Mapping
Converts `ErrorOr` errors to proper HTTP responses with [RFC 7807](https://www.rfc-editor.org/rfc/rfc7807)
ProblemDetails:
@@ -119,7 +119,7 @@ ProblemDetails:
[See ErrorMapping.cs](https://github.com/ANcpLua/ErrorOrX/src/ErrorOrX.Generators/Models/ErrorMapping.cs)
-### Request Validation
+######### Request Validation
Generated code validates before calling your handler:
@@ -128,7 +128,7 @@ Generated code validates before calling your handler:
- JSON deserialization (catches `JsonException`)
- Content-Type checking (returns 415 for wrong type)
-### OpenAPI Metadata
+######### OpenAPI Metadata
Full OpenAPI documentation without manual attributes:
@@ -140,7 +140,7 @@ Full OpenAPI documentation without manual attributes:
[See OpenApiTransformerGenerator.cs](https://github.com/ANcpLua/ErrorOrX/src/ErrorOrX.Generators/OpenApiTransformerGenerator.cs)
-### Builder API
+######### Builder API
Fluent configuration following ASP.NET Core patterns:
@@ -155,7 +155,7 @@ app.MapErrorOrEndpoints()
.RequireRateLimiting("api"); // Global rate limit
```
-### Analyzers (38 Diagnostics)
+######### Analyzers (38 Diagnostics)
Real-time IDE feedback covering:
@@ -171,7 +171,7 @@ Real-time IDE feedback covering:
[See Descriptors.cs](https://github.com/ANcpLua/ErrorOrX/src/ErrorOrX.Generators/Analyzers/Descriptors.cs)
-## Installation
+###### Installation
```bash
dotnet add package ErrorOrX.Generators
@@ -179,7 +179,7 @@ dotnet add package ErrorOrX.Generators
This package includes both the source generator and the `ErrorOrX` runtime library.
-## Quick Start
+###### Quick Start
```csharp
// Program.cs
@@ -208,7 +208,7 @@ public static class TodoApi
}
```
-## Error Types
+###### Error Types
Create structured errors mapped to HTTP status codes:
@@ -223,7 +223,7 @@ Error.Unexpected("Unknown", "An unexpected error occurred") // 500
Error.Custom(422, "Validation.Complex", "Complex validation failed")
```
-## Nullable-to-ErrorOr Extensions
+###### Nullable-to-ErrorOr Extensions
Convert nullable values to `ErrorOr` with auto-generated error codes:
@@ -250,7 +250,7 @@ return value.OrError(() => BuildExpensiveError()); // Lazy evaluation
| `.OrError(Error)` | Any | Any | Custom error |
| `.OrError(Func)` | Any | Any | Lazy custom error |
-## Fluent API
+###### Fluent API
Chain operations using railway-oriented programming patterns:
@@ -275,7 +275,7 @@ GetUser(id).Switch(
errors => Logger.LogError(errors.First().Description));
```
-## Result Markers
+###### Result Markers
Use semantic markers for endpoints without response bodies:
@@ -286,7 +286,7 @@ Result.Updated // 204 No Content
Result.Deleted // 204 No Content
```
-## Interface Types with `[ReturnsError]`
+###### Interface Types with `[ReturnsError]`
Document possible errors on interface methods for OpenAPI generation:
@@ -307,7 +307,7 @@ public static ErrorOr GetById(Guid id, ITodoService svc) =>
The generator reads `[ReturnsError]` attributes from interface/abstract methods to build the complete `Results<...>`
union for OpenAPI documentation.
-## Smart Parameter Binding
+###### Smart Parameter Binding
The generator automatically infers parameter sources:
@@ -325,7 +325,7 @@ public static ErrorOr GetById(
=> svc.GetById(id).OrNotFound();
```
-## Middleware Attributes
+###### Middleware Attributes
Standard ASP.NET Core attributes on your handler methods are translated to Minimal API fluent calls:
@@ -343,7 +343,7 @@ public static ErrorOr CreateAdmin(CreateUserRequest req) \{ }
// .CacheOutput(policy => policy.Expire(TimeSpan.FromSeconds(60)));
```
-## Native AOT
+###### Native AOT
Fully compatible with `PublishAot=true`. Create a `JsonSerializerContext` with your endpoint types:
@@ -373,14 +373,14 @@ app.Run();
The `[JsonSourceGenerationOptions]` on your context controls serialization behavior (camelCase, null handling).
The builder methods `WithCamelCase()` and `WithIgnoreNulls()` are only needed if you want to override at runtime.
-## Packages
+###### Packages
| Package | Target | Description |
|-----------------------|------------------|--------------------------------------|
| `ErrorOrX.Generators` | `netstandard2.0` | Source generator (includes ErrorOrX) |
| `ErrorOrX` | `net10.0` | Runtime library (auto-referenced) |
-## Changelog
+###### Changelog
See [CHANGELOG.md](https://github.com/ANcpLua/ErrorOrX/CHANGELOG.md) for version history.
diff --git a/v2/rscg_examples_site/docs/RSCG-Examples/KnockOff.md b/v2/rscg_examples_site/docs/RSCG-Examples/KnockOff.md
new file mode 100644
index 000000000..59401b130
--- /dev/null
+++ b/v2/rscg_examples_site/docs/RSCG-Examples/KnockOff.md
@@ -0,0 +1,1048 @@
+---
+sidebar_position: 2550
+title: 255 - KnockOff
+description: Generating test stubs with mocking for interfaces
+slug: /KnockOff
+---
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+import TOCInline from '@theme/TOCInline';
+import SameCategory from '../Categories/_PrimitiveTests.mdx';
+
+# KnockOff by Keith Voels
+
+
+
+
+## NuGet / site data
+[](https://www.nuget.org/packages/KnockOff/)
+[](https://github.com/NeatooDotNet/KnockOff)
+
+
+## Details
+
+### Info
+:::info
+
+Name: **KnockOff**
+
+A Roslyn Source Generator for creating unit test stubs. Unlike Moq's fluent runtime configuration, KnockOff uses partial classes for compile-time setup—trading flexibility for readability and performance.
+
+Author: Keith Voels
+
+NuGet:
+*https://www.nuget.org/packages/KnockOff/*
+
+
+You can find more details at https://github.com/NeatooDotNet/KnockOff
+
+Source: https://github.com/NeatooDotNet/KnockOff
+
+:::
+
+### Author
+:::note
+Keith Voels
+
+:::
+
+## Original Readme
+:::note
+
+### KnockOff
+
+A .NET mocking library that lets you define reusable stub classes — with full mocking capabilities built in.
+
+Define your test double once. Reuse it across your test project. Customize it per-test with Return, Call, Verify, and When chains. No more copying mock setups between tests or maintaining shared factory methods full of `Arg.Any<>()`.
+
+Powered by Roslyn source generation for [tighter type safety](https://github.com/NeatooDotNet/KnockOff/docs/type-safety.md) — more issues surface as compile errors instead of runtime surprises.
+
+Claude Code was used to write this library. Skip to more [AI discussion](#ai).
+
+[](https://www.nuget.org/packages/KnockOff/)
+[](https://github.com/NeatooDotNet/KnockOff/actions)
+[](https://opensource.org/licenses/MIT)
+
+
+###### KnockOff Stub
+
+There are [9 patterns](https://github.com/NeatooDotNet/KnockOff/docs/guides/stub-patterns.md) total, including a [standard fluent mocking approach](https://github.com/NeatooDotNet/KnockOff/docs/guides/stub-patterns.md#inline-interface-pattern) with inline stubs. But reusable stub classes are where KnockOff stands apart:
+
+
+```cs
+[KnockOff]
+public partial class MyRepoStub(List Users) : IMyRepo
+{
+ protected override User? GetUser_(int id)
+ {
+ return Users.Single(u => u.Id == id);
+ }
+
+ protected override void Update_(User user)
+ {
+ Assert.Contains(user, Users);
+ }
+}
+```
+
+
+- **`[KnockOff]` + `partial class`** — KnockOff generates a base class that implements every member of `IMyRepo`. Your stub is a real class — define it once, reuse it across your entire test project. Pass it around, register it in DI, share it between test fixtures.
+- **Constructor parameters** — `List Users` is a primary constructor. Test data flows in naturally, just like any other C# class.
+- **Overrides are optional** — `GetUser_` and `Update_` override the generated defaults. Only override what you need — everything else still works with [Return/Call](https://github.com/NeatooDotNet/KnockOff/docs/guides/methods.md), [Return(value)](https://github.com/NeatooDotNet/KnockOff/docs/reference/interceptor-api.md), or [When chains](https://github.com/NeatooDotNet/KnockOff/docs/guides/parameter-matching.md).
+- **Tighter type safety** — Every Return, Call, and When call is complete in a single step — no forgotten `.Returns()` that [silently breaks at runtime](https://github.com/NeatooDotNet/KnockOff/docs/type-safety.md). No manual `` type parameters that can drift. [Details →](https://github.com/NeatooDotNet/KnockOff/docs/type-safety.md)
+
+This stub is also a full mock. It has [Verify](https://github.com/NeatooDotNet/KnockOff/docs/guides/verification.md), [Strict mode](https://github.com/NeatooDotNet/KnockOff/docs/guides/strict-mode.md), [Async](https://github.com/NeatooDotNet/KnockOff/docs/guides/async-patterns.md), and [Source Delegation](https://github.com/NeatooDotNet/KnockOff/docs/guides/source-delegation.md) — all on the same reusable class.
+
+
+###### Why I Wrote KnockOff
+
+I often wanted to reuse my mocks.
+Especially in my integration test library where I may even register my mocks.
+I found myself either copying my mock definitions code or creating shared methods like this:
+
+**NSubstitute:**
+
+```cs
+public static IMyRepo NSubstituteMock(List users)
+{
+ var myRepoMock = Substitute.For();
+
+ // Setup: configure GetUser to look up from the list based on id
+ myRepoMock.GetUser(Arg.Any())
+ .Returns(callInfo => users.SingleOrDefault(u => u.Id == callInfo.Arg()));
+
+ // Setup: configure Update to assert user exists in list
+ myRepoMock.When(x => x.Update(Arg.Any()))
+ .Do(callInfo => Assert.Contains(callInfo.Arg(), users));
+
+ return myRepoMock;
+}
+```
+
+
+Here's another [example from PowerToys](https://github.com/microsoft/PowerToys/blob/main/src/settings-ui/Settings.UI.UnitTests/Mocks/ISettingsUtilsMocks.cs).
+
+But I find that hard to read and unintuitive. Also, my shared methods accumulated extra parameters for variations across different tests.
+
+
+###### So I Created KnockOff
+
+You can create a stub to implement [interfaces](https://github.com/NeatooDotNet/KnockOff/docs/guides/stub-patterns.md) or non-sealed [classes](https://github.com/NeatooDotNet/KnockOff/docs/guides/stub-patterns.md) with virtual methods.
+Yet, you can still customize the stub per test.
+All while having the features you would expect with a full mocking library.
+
+With the stub above, your tests are:
+
+
+```cs
+var myRepoKO = new MyRepoStub([new User \{ Id = 1 }, new User \{ Id = 2 }]);
+var userDomainModel = new UserDomainModel(myRepoKO);
+
+Assert.True(userDomainModel.Fetch(1));
+
+// I have Verify on my Stub!
+myRepoKO.GetUser.Verify(Called.Once);
+```
+
+
+Need different behavior for a specific test? Override with Return/Call:
+
+
+```cs
+var user1 = new User \{ Id = 1 }; // Ignored do to per-test configuration
+var myRepoKO = new MyRepoStub([user1]);
+var userDomainModel = new UserDomainModel(myRepoKO);
+
+var user2 = new User \{ Id = 2 };
+
+// When and Return overrides the stub methods
+myRepoKO.GetUser.When(2).Return(user2).Verifiable();
+myRepoKO.Update.Call(u => Assert.Same(u, user2)).Verifiable();
+
+userDomainModel.Fetch(2);
+userDomainModel.Update();
+
+myRepoKO.Verify();
+```
+
+
+**Now I have my stubs and mocks in one!**
+
+---
+
+###### What Sets KnockOff Apart
+
+- **[Reusable stub classes](https://github.com/NeatooDotNet/KnockOff/docs/guides/reusable-stubs.md)** — Define once, customize per-test. Your stub is a real class — pass it through constructors, register it in DI.
+- **[Source delegation](https://github.com/NeatooDotNet/KnockOff/docs/guides/source-delegation.md)** — Delegate to a real implementation, override only specific methods. No equivalent in Moq or NSubstitute.
+- **[Protected methods](https://github.com/NeatooDotNet/KnockOff/docs/guides/protected-methods.md)** — Same `Return`/`Call`/`Verify` API, fully typed. No string-based names, no manual subclasses.
+- **[Ref/out parameters](https://github.com/NeatooDotNet/KnockOff/docs/guides/ref-out-parameters.md)** — Natural lambda syntax with `ref`/`out` keywords. No special matchers or index-based access.
+- **[Multiple interfaces](https://github.com/NeatooDotNet/KnockOff/docs/guides/multiple-interfaces.md)** — Unified interceptors on one stub. No `.As()` references or casting.
+- **[Tighter type safety](https://github.com/NeatooDotNet/KnockOff/docs/type-safety.md)** — Each Return/Call/When call is complete in one step — no forgotten `.Returns()` that silently breaks at runtime.
+- **[Parameter matching](https://github.com/NeatooDotNet/KnockOff/docs/guides/parameter-matching-comparison.md)** — `Return((a, b) => a > 0 ? 100 : 0)` — standard C# conditionals instead of `Arg.Is<>` or `It.Is<>` per parameter.
+- **Built-in argument capture** — `LastArg`, `LastArgs`, `LastSetValue`, `LastSetEntry` — no manual `Arg.Do<>` or `Callback<>` setup.
+- **Event verification** — `VerifyAdd()` / `VerifyRemove()` / `HasSubscribers` — not available in Moq or NSubstitute.
+- **Explicit Get/Set verification** — `VerifyGet(Called)` / `VerifySet(Called)` for properties and indexers.
+- **Stubbing concrete classes** — Override virtual methods on non-sealed classes with the same API.
+
+---
+
+###### Quick Start
+
+######### Install
+
+```bash
+dotnet add package KnockOff
+```
+
+######### Create a Stub
+
+
+```cs
+public interface IQuickStartRepo
+{
+ User? GetUser(int id);
+}
+
+[KnockOff]
+public partial class QuickStartRepoStub : IQuickStartRepo \{ }
+
+public class QuickStartCreateStubTests
+{
+ [Fact]
+ public void CreateStub_IsReady()
+ {
+ var stub = new QuickStartRepoStub();
+
+ IQuickStartRepo repository = stub;
+ Assert.NotNull(repository);
+ }
+}
+```
+
+
+######### Configure and Verify
+
+
+```cs
+[Fact]
+public void ConfigureStub_WithReturn()
+{
+ var stub = new QuickStartRepoStub();
+
+ stub.GetUser.Return((id) => new User \{ Id = id, Name = "Test User" });
+
+ IQuickStartRepo repository = stub;
+ var user = repository.GetUser(42);
+
+ Assert.NotNull(user);
+ Assert.Equal(42, user.Id);
+ Assert.Equal("Test User", user.Name);
+}
+```
+
+
+
+```cs
+[Fact]
+public void VerifyCalls_WithVerifiable()
+{
+ var stub = new QuickStartRepoStub();
+ stub.GetUser.Return((id) => new User \{ Id = id, Name = "Test" }).Verifiable();
+
+ IQuickStartRepo repository = stub;
+
+ var user = repository.GetUser(42);
+
+ // Verify() checks all members marked with .Verifiable()
+ stub.Verify();
+}
+```
+
+
+---
+
+###### The Difference
+
+**Moq:**
+```cs
+mock.Setup(x => x.GetUser(It.Is(id => id > 0)))
+ .Returns(id => new User \{ Id = id });
+```
+
+**NSubstitute:**
+
+```cs
+var repo = Substitute.For();
+repo.GetUser(Arg.Is(id => id > 0)).Returns(x => new User \{ Id = x.Arg() });
+```
+
+
+**KnockOff:**
+
+```cs
+var stub = new CompareUserRepoStub();
+stub.GetUser.Return((id) => id > 0 ? new User \{ Id = id \} : null);
+```
+
+
+No `It.Is<>()`. No `Arg.Is<>()`. No `x.Arg()`. The parameter is just `id`.
+
+---
+
+For side-by-side comparison tables (methods, properties, events, delegates, indexers), see the [complete comparison guide](https://github.com/NeatooDotNet/KnockOff/docs/comparison.md).
+
+---
+
+###### Argument Matching
+
+**Moq:**
+```cs
+// Moq - It.Is per parameter
+mock.Setup(x => x.Add(It.Is(a => a > 0), It.IsAny())).Returns(100);
+```
+
+**NSubstitute:**
+
+```cs
+// NSubstitute - Arg.Is per parameter (permanent matchers)
+calc.Add(Arg.Is(a => a > 0), Arg.Any()).Returns(100);
+```
+
+
+**KnockOff:**
+
+```cs
+// KnockOff - Returns with conditional (permanent, matches all calls)
+stub.Add.Return((a, b) => a > 0 ? 100 : 0);
+```
+
+
+
+```cs
+// KnockOff - When() for sequential matching (first match returns 100, then falls through)
+stub.Add.When((a, b) => a > 0).Return(100).ThenCall((a, b) => a + b);
+```
+
+
+**Multiple specific values:**
+
+**Moq:**
+```cs
+mock.Setup(x => x.Add(1, 2)).Returns(100);
+mock.Setup(x => x.Add(3, 4)).Returns(200);
+```
+
+
+```cs
+// Multiple specific values
+calc.Add(1, 2).Returns(100);
+calc.Add(3, 4).Returns(200);
+```
+
+
+
+```cs
+stub.Add.When(1, 2).Return(100);
+stub.Add.When(3, 4).Return(200);
+```
+
+
+**Note:** Moq and NSubstitute matchers are permanent -- they match all qualifying calls. KnockOff's `When()` is sequential -- matchers are consumed in order. Use `Return(callback)` with conditionals for permanent matching behavior.
+
+######### Argument Capture
+
+**Moq:**
+```cs
+// Moq - requires Callback setup
+int capturedA = 0, capturedB = 0;
+mock.Setup(x => x.Add(It.IsAny(), It.IsAny()))
+ .Callback((a, b) => \{ capturedA = a; capturedB = b; });
+mock.Object.Add(1, 2);
+```
+
+**NSubstitute:**
+
+```cs
+// NSubstitute - requires Arg.Do in setup
+int capturedA = 0, capturedB = 0;
+calc.Add(Arg.Do(x => capturedA = x), Arg.Do(x => capturedB = x));
+calc.Add(1, 2);
+```
+
+
+**KnockOff:**
+
+```cs
+// KnockOff - built-in, no pre-setup
+var tracking = stub.Add.Return((a, b) => a + b);
+ICalculator calc = stub;
+calc.Add(1, 2);
+var (a, b) = tracking.LastArgs; // Named tuple: a = 1, b = 2
+```
+
+
+For full comparisons of properties, events, delegates, and indexers, see the [complete comparison guide](https://github.com/NeatooDotNet/KnockOff/docs/comparison.md).
+
+---
+
+###### Method Overload Resolution
+
+**The Problem:** When an interface has overloaded methods with the same parameter count but different types:
+
+
+```cs
+public interface IFormatter
+{
+ string Format(string input, bool uppercase);
+ string Format(string input, int maxLength);
+}
+```
+
+
+######### Any-Value Matching
+
+**Moq:**
+```cs
+// It.IsAny() required - compiler needs the types to resolve overload
+mock.Setup(x => x.Format(It.IsAny(), It.IsAny())).Returns("bool overload");
+mock.Setup(x => x.Format(It.IsAny(), It.IsAny())).Returns("int overload");
+```
+
+**NSubstitute:**
+
+```cs
+// Arg.Any() required - compiler needs the types to resolve overload
+formatter.Format(Arg.Any(), Arg.Any()).Returns("bool overload");
+formatter.Format(Arg.Any(), Arg.Any()).Returns("int overload");
+```
+
+
+**KnockOff:**
+
+```cs
+// Explicit parameter types resolve the overload - standard C# syntax
+stub.Format.Return((string input, bool uppercase) => "bool overload");
+stub.Format.Return((string input, int maxLength) => "int overload");
+```
+
+
+######### Specific-Value Matching
+
+**NSubstitute:**
+
+```cs
+// Specific value matching - literals work when all args are specific
+formatter.Format("test", true).Returns("UPPERCASE");
+formatter.Format("test", 10).Returns("truncated");
+```
+
+
+**KnockOff:**
+
+```cs
+// Specific value matching - parameter types resolve the overload
+stub.Format.When("test", true).Return("UPPERCASE");
+stub.Format.When("test", 10).Return("truncated");
+```
+
+
+######### Argument Access
+
+**Moq:**
+```cs
+// To use argument values, extract via Returns:
+mock.Setup(x => x.Format(It.IsAny(), It.IsAny()))
+ .Returns((input, uppercase) => uppercase ? input.ToUpper() : input);
+```
+
+**NSubstitute:**
+
+```cs
+// To use argument values, extract from CallInfo:
+formatter.Format(Arg.Any(), Arg.Any())
+ .Returns(x => x.ArgAt(1) ? x.ArgAt(0).ToUpper() : x.ArgAt(0));
+```
+
+
+**KnockOff:**
+
+```cs
+// Arguments are directly available with names and types:
+stub.Format.Return((string input, bool uppercase) => uppercase ? input.ToUpper() : input);
+```
+
+
+**The Difference:**
+- Moq: `It.IsAny()` + `.Returns((input, uppercase) => ...)` to match any value and access arguments
+- NSubstitute: `Arg.Any()` + `x.ArgAt(1)` to match any value and access arguments
+- KnockOff: `(string input, bool uppercase)` - standard C# lambda with named, typed parameters
+
+---
+
+###### Three Stub Patterns
+
+KnockOff supports [9 patterns](https://github.com/NeatooDotNet/KnockOff/docs/guides/stub-patterns.md) total. Here are the three most common:
+
+**[Standalone](https://github.com/NeatooDotNet/KnockOff/docs/guides/stub-patterns.md#standalone-pattern)** - Reusable across your project:
+
+```cs
+[KnockOff]
+public partial class ReadmeStandaloneStub : IUserRepo \{ }
+```
+
+
+**[Inline Interface](https://github.com/NeatooDotNet/KnockOff/docs/guides/stub-patterns.md#inline-interface-pattern)** - Test-local stubs:
+
+```cs
+[Fact]
+public void InlineInterface_Pattern()
+{
+ var stub = new Stubs.IUserRepo();
+ stub.GetUser.Return((id) => new User \{ Id = id });
+
+ IUserRepo repo = stub;
+ Assert.NotNull(repo.GetUser(1));
+}
+```
+
+
+**[Inline Class](https://github.com/NeatooDotNet/KnockOff/docs/guides/stub-patterns.md#inline-class-pattern)** - Stub virtual members:
+
+```cs
+[Fact]
+public void InlineClass_Pattern()
+{
+ var stub = new Stubs.MyService();
+ stub.GetUser.Return((id) => new User \{ Id = id });
+
+ MyService service = stub.Object;
+ Assert.NotNull(service.GetUser(1));
+}
+```
+
+
+---
+
+###### Roslyn Source Generation
+
+KnockOff uses Roslyn source generation, which means:
+
+- No more `Arg.Any<>()`. No more `It.IsAny<>()`. Just write C#
+- If the method signature changes you get a compile error
+- There's a small performance gain but honestly it's negligible
+
+Source generation opens doors beyond traditional mocking — I've already added [9 patterns](https://github.com/NeatooDotNet/KnockOff/docs/guides/stub-patterns.md) and features like [Source Delegation](https://github.com/NeatooDotNet/KnockOff/docs/guides/source-delegation.md), with more ideas to come.
+
+**What other ideas do you have?** Open a [discussion](https://github.com/NeatooDotNet/KnockOff/discussions).
+
+
+###### AI
+
+This is an idea I've had for years but never took the time to implement. With my ideas and guidance, Claude Code has written the entirety of this library — the Roslyn source generator, the runtime library, the tests, and the documentation.
+
+Source generation turned out to be a great fit for AI code generation. The work is highly patterned: analyze an interface, generate code for each member, handle edge cases across 9 patterns and 4 member types. That's exactly the kind of systematic, repetitive-but-varied work where AI excels. I designed the API and patterns; Claude Code implemented them across every combination.
+
+######### Claude Code Skill
+
+KnockOff includes a [Claude Code skill](https://github.com/NeatooDotNet/KnockOff/skillsknockoff/) that teaches Claude how to use the library. Copy the `skills/knockoff/` directory into your project and Claude Code will know how to create stubs, configure behavior, write tests with KnockOff, and migrate from Moq — without you explaining the API.
+
+The skill includes slash commands:
+- **`/knockoff:create-stub`** — Create a new stub class with the pattern of your choice
+- **`/knockoff:migrate-from-moq`** — Convert existing Moq tests to KnockOff
+- **`/knockoff:troubleshoot`** — Diagnose and fix common KnockOff issues
+
+---
+
+###### Documentation
+
+- **[Getting Started](https://github.com/NeatooDotNet/KnockOff/docs/getting-started.md)** - Installation and first stub
+- **[Stub Patterns](https://github.com/NeatooDotNet/KnockOff/docs/guides/stub-patterns.md)** - Standalone, inline interface, inline class
+- **[Interceptor API](https://github.com/NeatooDotNet/KnockOff/docs/reference/interceptor-api.md)** - Complete `Returns`, `Execute`, `Get`, `Set` reference
+- **[Source Delegation](https://github.com/NeatooDotNet/KnockOff/docs/guides/source-delegation.md)** - Delegate to real implementations
+- **[Full Comparison Guide](https://github.com/NeatooDotNet/KnockOff/docs/comparison.md)** - Properties, events, delegates, indexers vs Moq and NSubstitute
+- **[Migration from Moq](https://github.com/NeatooDotNet/KnockOff/docs/migration/from-moq.md)** - Step-by-step migration guide
+- **[Migration from NSubstitute](https://github.com/NeatooDotNet/KnockOff/docs/migration/from-nsubstitute.md)** - Comparison and migration guide
+
+---
+
+###### License
+
+MIT License. See [LICENSE](https://github.com/NeatooDotNet/KnockOff/LICENSE) for details.
+
+---
+
+###### Contributing
+
+Contributions welcome! See [CONTRIBUTING.md](https://github.com/NeatooDotNet/KnockOff/CONTRIBUTING.md) for guidelines.
+
+- **Issues**: [GitHub Issues](https://github.com/NeatooDotNet/KnockOff/issues)
+- **Pull Requests**: Bug fixes, features, documentation
+- **Discussions**: [GitHub Discussions](https://github.com/NeatooDotNet/KnockOff/discussions)
+
+
+:::
+
+### About
+:::note
+
+Generating test stubs with mocking for interfaces
+
+
+:::
+
+## How to use
+
+### Example (source csproj, source files)
+
+
+
+
+
+This is the CSharp Project that references **KnockOff**
+```xml showLineNumbers {13}
+
+
+
+ net10.0
+ enable
+ enable
+
+ false
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+ $(BaseIntermediateOutputPath)\GX
+
+
+
+
+```
+
+
+
+
+
+ This is the use of **KnockOff** in *IMyClock.cs*
+
+```csharp showLineNumbers
+namespace MockData;
+
+public interface IMyClock
+{
+ public DateTime GetNow();
+ public DateTime GetUtcNow();
+}
+```
+
+
+
+
+ This is the use of **KnockOff** in *TestClock.cs*
+
+```csharp showLineNumbers
+
+using KnockOff;
+
+namespace TestClock;
+
+[KnockOff]
+public partial class QuickStartRepoStub : IMyClock \{ }
+
+
+[TestClass]
+public class TestClock
+{
+ [TestMethod]
+ public void TestMyClock()
+ {
+ var expectations = new QuickStartRepoStub();
+ expectations.GetNow.Return(DateTime.Now.AddYears(-1));
+
+ IMyClock mock = expectations;
+ var data= mock.GetNow();
+ Assert.AreEqual(DateTime.Now.Year -1, data.Year);
+ expectations.Verify();
+ }
+}
+
+
+
+```
+
+
+
+
+### Generated Files
+
+Those are taken from $(BaseIntermediateOutputPath)\GX
+
+
+
+
+```csharp showLineNumbers
+//
+#nullable enable
+
+namespace TestClock;
+
+public class QuickStartRepoStubBase
+{
+ /// Override to provide default implementation for global::MockData.IMyClock.GetNow.
+ protected virtual global::System.DateTime GetNow_() => default!;
+
+ /// Override to provide default implementation for global::MockData.IMyClock.GetUtcNow.
+ protected virtual global::System.DateTime GetUtcNow_() => default!;
+
+}
+
+```
+
+
+
+
+```csharp showLineNumbers
+//
+#nullable enable
+
+using System.Linq;
+
+namespace TestClock;
+
+partial class QuickStartRepoStub : QuickStartRepoStubBase, global::MockData.IMyClock, global::KnockOff.IKnockOffStub
+{
+ /// Tracks and configures behavior for GetNow.
+ public sealed class GetNowInterceptor : global::KnockOff.Interceptors.MethodInterceptorBase
+ {
+ /// Source object to delegate to when no callback is configured.
+ internal global::MockData.IMyClock? _source;
+
+ /// Delegate for GetNow.
+ public delegate global::System.DateTime GetNowDelegate();
+
+ public GetNowInterceptor() : base("GetNow") \{ }
+
+ protected override global::System.DateTime InvokeDelegate(GetNowDelegate del, global::KnockOff.Unit args) => del();
+ protected override GetNowDelegate CreateValueDelegate(global::System.DateTime value) => () => value;
+ protected override void RecordArgs(global::KnockOff.Unit args, MethodCallBuilderBase tracking) \{ }
+ protected override void RecordUnconfiguredArgs(global::KnockOff.Unit args) \{ }
+
+ /// Configures callback that repeats indefinitely. Returns builder for sequence chaining.
+ public MethodCallBuilderImpl Return(GetNowDelegate callback)
+ {
+ var builder = new MethodCallBuilderImpl(this);
+ SetupReturnCallback(callback, builder);
+ return builder;
+ }
+
+ /// Configures return value that repeats indefinitely. Returns builder for sequence chaining.
+ public MethodCallBuilderImpl Return(global::System.DateTime value)
+ {
+ var builder = new MethodCallBuilderImpl(this);
+ SetupReturnValue(value, builder);
+ return builder;
+ }
+
+ /// Configures sequence of return values. Each value returned once, last repeats.
+ public ReturnMethodSequenceBase Return(global::System.DateTime first, params global::System.DateTime[] rest)
+ {
+ var builder = Return(() => first);
+ if (rest.Length == 0)
+ {
+ return builder.ThenReturn(first);
+ }
+ var seq = builder.ThenReturn(rest[0]);
+ for (int i = 1; i < rest.Length; i++)
+ {
+ seq.ThenReturn(rest[i]);
+ }
+ return seq;
+ }
+
+ /// Invokes the configured callback. Called by explicit interface implementation.
+ internal global::System.DateTime Invoke(bool strict)
+ {
+ var (handled, result) = RunPriorityChain(default);
+ if (handled) return result;
+ _unconfiguredCallCount++;
+ RecordUnconfiguredArgs(default);
+ var (seqHandled, seqResult) = HandleNonVoidSequenceExhaustedRepeat(strict, default);
+ if (seqHandled) return seqResult;
+ #pragma warning disable CS8601, SYSLIB0050
+ if (_source is \{ \} src) return src.GetNow();
+ #pragma warning restore CS8601, SYSLIB0050
+ if (strict) throw global::KnockOff.StubException.NotConfigured("", "GetNow");
+ return default!;
+ }
+
+ /// Resets tracking state but preserves configuration and verifiable marking.
+ public override void Reset()
+ {
+ base.Reset();
+ _source = null;
+ }
+
+ /// Builder for callback registration. Supports tracking and lazy elevation to sequence.
+ public sealed class MethodCallBuilderImpl : ReturnMethodCallBuilderBase, global::KnockOff.IMethodReturnBuilder
+ {
+ private readonly GetNowInterceptor _typedInterceptor;
+
+ public MethodCallBuilderImpl(GetNowInterceptor interceptor) : base(interceptor)
+ {
+ _typedInterceptor = interceptor;
+ }
+
+
+ public override void Reset() => base.Reset();
+
+ /// Elevates to sequence mode and adds another callback. Returns sequence for further chaining.
+ public ReturnMethodSequenceBase ThenReturn(GetNowDelegate callback)
+ {
+ return ThenReturnBase(callback);
+ }
+
+ /// Elevates to sequence mode and adds a value. Returns sequence for further chaining.
+ public ReturnMethodSequenceBase ThenReturn(global::System.DateTime value) => ThenReturn(() => value);
+
+ /// Adds multiple values to the sequence. Each value returned once.
+ public ReturnMethodSequenceBase ThenReturn(params global::System.DateTime[] values)
+ {
+ if (values.Length == 0) \{ ElevateToSequenceBase(); return new ReturnMethodSequenceBase(_typedInterceptor, CreateNextReturnBuilder); }
+ var seq = ThenReturn(values[0]);
+ for (int i = 1; i < values.Length; i++) seq.ThenReturn(values[i]);
+ return seq;
+ }
+
+ /// Marks for verification by Stub.Verify().
+ public MethodCallBuilderImpl Verifiable() \{ VerifiableBase(); return this; }
+ /// Marks for verification by Stub.Verify() with Called constraint.
+ public MethodCallBuilderImpl Verifiable(global::KnockOff.Called times) \{ VerifiableBase(times); return this; }
+
+ protected override ReturnMethodCallBuilderBase CreateNextReturnBuilder() => new MethodCallBuilderImpl(_typedInterceptor);
+
+ global::KnockOff.IMethodTracking global::KnockOff.IMethodTracking.Verifiable() => Verifiable();
+ global::KnockOff.IMethodTracking global::KnockOff.IMethodTracking.Verifiable(global::KnockOff.Called times) => Verifiable(times);
+ global::KnockOff.IMethodReturnBuilder global::KnockOff.IMethodReturnBuilder.Verifiable() => Verifiable();
+ global::KnockOff.IMethodReturnBuilder global::KnockOff.IMethodReturnBuilder.Verifiable(global::KnockOff.Called times) => Verifiable(times);
+ global::KnockOff.IMethodReturnSequence global::KnockOff.IMethodReturnBuilder.ThenReturn(GetNowDelegate callback) => ThenReturn(callback);
+ }
+
+ }
+
+ /// Tracks and configures behavior for GetUtcNow.
+ public sealed class GetUtcNowInterceptor : global::KnockOff.Interceptors.MethodInterceptorBase
+ {
+ /// Source object to delegate to when no callback is configured.
+ internal global::MockData.IMyClock? _source;
+
+ /// Delegate for GetUtcNow.
+ public delegate global::System.DateTime GetUtcNowDelegate();
+
+ public GetUtcNowInterceptor() : base("GetUtcNow") \{ }
+
+ protected override global::System.DateTime InvokeDelegate(GetUtcNowDelegate del, global::KnockOff.Unit args) => del();
+ protected override GetUtcNowDelegate CreateValueDelegate(global::System.DateTime value) => () => value;
+ protected override void RecordArgs(global::KnockOff.Unit args, MethodCallBuilderBase tracking) \{ }
+ protected override void RecordUnconfiguredArgs(global::KnockOff.Unit args) \{ }
+
+ /// Configures callback that repeats indefinitely. Returns builder for sequence chaining.
+ public MethodCallBuilderImpl Return(GetUtcNowDelegate callback)
+ {
+ var builder = new MethodCallBuilderImpl(this);
+ SetupReturnCallback(callback, builder);
+ return builder;
+ }
+
+ /// Configures return value that repeats indefinitely. Returns builder for sequence chaining.
+ public MethodCallBuilderImpl Return(global::System.DateTime value)
+ {
+ var builder = new MethodCallBuilderImpl(this);
+ SetupReturnValue(value, builder);
+ return builder;
+ }
+
+ /// Configures sequence of return values. Each value returned once, last repeats.
+ public ReturnMethodSequenceBase Return(global::System.DateTime first, params global::System.DateTime[] rest)
+ {
+ var builder = Return(() => first);
+ if (rest.Length == 0)
+ {
+ return builder.ThenReturn(first);
+ }
+ var seq = builder.ThenReturn(rest[0]);
+ for (int i = 1; i < rest.Length; i++)
+ {
+ seq.ThenReturn(rest[i]);
+ }
+ return seq;
+ }
+
+ /// Invokes the configured callback. Called by explicit interface implementation.
+ internal global::System.DateTime Invoke(bool strict)
+ {
+ var (handled, result) = RunPriorityChain(default);
+ if (handled) return result;
+ _unconfiguredCallCount++;
+ RecordUnconfiguredArgs(default);
+ var (seqHandled, seqResult) = HandleNonVoidSequenceExhaustedRepeat(strict, default);
+ if (seqHandled) return seqResult;
+ #pragma warning disable CS8601, SYSLIB0050
+ if (_source is \{ \} src) return src.GetUtcNow();
+ #pragma warning restore CS8601, SYSLIB0050
+ if (strict) throw global::KnockOff.StubException.NotConfigured("", "GetUtcNow");
+ return default!;
+ }
+
+ /// Resets tracking state but preserves configuration and verifiable marking.
+ public override void Reset()
+ {
+ base.Reset();
+ _source = null;
+ }
+
+ /// Builder for callback registration. Supports tracking and lazy elevation to sequence.
+ public sealed class MethodCallBuilderImpl : ReturnMethodCallBuilderBase, global::KnockOff.IMethodReturnBuilder
+ {
+ private readonly GetUtcNowInterceptor _typedInterceptor;
+
+ public MethodCallBuilderImpl(GetUtcNowInterceptor interceptor) : base(interceptor)
+ {
+ _typedInterceptor = interceptor;
+ }
+
+
+ public override void Reset() => base.Reset();
+
+ /// Elevates to sequence mode and adds another callback. Returns sequence for further chaining.
+ public ReturnMethodSequenceBase ThenReturn(GetUtcNowDelegate callback)
+ {
+ return ThenReturnBase(callback);
+ }
+
+ /// Elevates to sequence mode and adds a value. Returns sequence for further chaining.
+ public ReturnMethodSequenceBase ThenReturn(global::System.DateTime value) => ThenReturn(() => value);
+
+ /// Adds multiple values to the sequence. Each value returned once.
+ public ReturnMethodSequenceBase ThenReturn(params global::System.DateTime[] values)
+ {
+ if (values.Length == 0) \{ ElevateToSequenceBase(); return new ReturnMethodSequenceBase(_typedInterceptor, CreateNextReturnBuilder); }
+ var seq = ThenReturn(values[0]);
+ for (int i = 1; i < values.Length; i++) seq.ThenReturn(values[i]);
+ return seq;
+ }
+
+ /// Marks for verification by Stub.Verify().
+ public MethodCallBuilderImpl Verifiable() \{ VerifiableBase(); return this; }
+ /// Marks for verification by Stub.Verify() with Called constraint.
+ public MethodCallBuilderImpl Verifiable(global::KnockOff.Called times) \{ VerifiableBase(times); return this; }
+
+ protected override ReturnMethodCallBuilderBase CreateNextReturnBuilder() => new MethodCallBuilderImpl(_typedInterceptor);
+
+ global::KnockOff.IMethodTracking global::KnockOff.IMethodTracking.Verifiable() => Verifiable();
+ global::KnockOff.IMethodTracking global::KnockOff.IMethodTracking.Verifiable(global::KnockOff.Called times) => Verifiable(times);
+ global::KnockOff.IMethodReturnBuilder global::KnockOff.IMethodReturnBuilder.Verifiable() => Verifiable();
+ global::KnockOff.IMethodReturnBuilder global::KnockOff.IMethodReturnBuilder.Verifiable(global::KnockOff.Called times) => Verifiable(times);
+ global::KnockOff.IMethodReturnSequence global::KnockOff.IMethodReturnBuilder.ThenReturn(GetUtcNowDelegate callback) => ThenReturn(callback);
+ }
+
+ }
+
+ /// Interceptor for GetNow.
+ public GetNowInterceptor GetNow \{ get; \} = new();
+
+ /// Interceptor for GetUtcNow.
+ public GetUtcNowInterceptor GetUtcNow \{ get; \} = new();
+
+ /// When true, throws StubException for unconfigured member access.
+ public bool Strict \{ get; set; \} = false;
+
+ /// The global::MockData.IMyClock instance. Use for passing to code expecting the interface.
+ public global::MockData.IMyClock Object => this;
+
+ /// Verifies all members marked with .Verifiable() were invoked as expected. Throws VerificationException with all failures if any fail.
+ public void Verify()
+ {
+ var failures = new global::System.Collections.Generic.List();
+
+ if (GetNow.CheckVerification() is \{ \} getnowFailure) failures.Add(getnowFailure);
+ if (GetUtcNow.CheckVerification() is \{ \} getutcnowFailure) failures.Add(getutcnowFailure);
+
+ if (failures.Count > 0)
+ throw new global::KnockOff.VerificationException(failures);
+ }
+
+ /// Verifies ALL configured members were invoked at least once. Throws VerificationException with all failures if any fail.
+ public void VerifyAll()
+ {
+ var failures = new global::System.Collections.Generic.List();
+
+ if (GetNow.CheckVerificationAll() is \{ \} getnowFailure) failures.Add(getnowFailure);
+ if (GetUtcNow.CheckVerificationAll() is \{ \} getutcnowFailure) failures.Add(getutcnowFailure);
+
+ if (failures.Count > 0)
+ throw new global::KnockOff.VerificationException(failures);
+ }
+
+ // Source(T) methods for interface delegation
+
+ /// Delegates unconfigured member access to the provided source object (global::MockData.IMyClock).
+ /// The source to delegate to, or null to clear.
+ public void Source(global::MockData.IMyClock? source)
+ {
+ GetNow._source = source;
+ GetUtcNow._source = source;
+ }
+
+ global::System.DateTime global::MockData.IMyClock.GetNow()
+ {
+ return GetNow.Invoke(Strict);
+ }
+
+ global::System.DateTime global::MockData.IMyClock.GetUtcNow()
+ {
+ return GetUtcNow.Invoke(Strict);
+ }
+
+}
+
+```
+
+
+
+
+## Useful
+
+### Download Example (.NET C#)
+
+:::tip
+
+[Download Example project KnockOff ](/sources/KnockOff.zip)
+
+:::
+
+
+### Share KnockOff
+
+
+
+https://ignatandrei.github.io/RSCG_Examples/v2/docs/KnockOff
+
+
+
diff --git a/v2/rscg_examples_site/docs/RSCG-Examples/index.md b/v2/rscg_examples_site/docs/RSCG-Examples/index.md
index 7f645cfd2..dac55d475 100644
--- a/v2/rscg_examples_site/docs/RSCG-Examples/index.md
+++ b/v2/rscg_examples_site/docs/RSCG-Examples/index.md
@@ -1,7 +1,7 @@
---
sidebar_position: 30
-title: 254 RSCG list by category
-description: 254 RSCG list by category
+title: 255 RSCG list by category
+description: 255 RSCG list by category
slug: /rscg-examples
---
@@ -1522,7 +1522,7 @@ import DocCardList from '@theme/DocCardList';
## Tests
- Expand Tests =>examples:7
+ Expand Tests =>examples:8
@@ -1558,6 +1558,11 @@ import DocCardList from '@theme/DocCardList';
[Imposter](/docs/Imposter)
+
+
+
+[KnockOff](/docs/KnockOff)
+
@@ -2100,6 +2105,8 @@ flowchart LR;
Tests--> Imposter((Imposter))
+ Tests--> KnockOff((KnockOff))
+
Validator--> validly((validly))
WinAPI--> Com((Com))
diff --git a/v2/rscg_examples_site/docs/about.md b/v2/rscg_examples_site/docs/about.md
index 4ad1df422..b81a91fe8 100644
--- a/v2/rscg_examples_site/docs/about.md
+++ b/v2/rscg_examples_site/docs/about.md
@@ -6,7 +6,7 @@ title: About
## Content
You will find here code examples
-of 254 Roslyn Source Code Generator (RSCG)
+of 255 Roslyn Source Code Generator (RSCG)
that can be useful for you. That means, you will write more elegant and concise code - even if the generators code is not always nice to look.
## Are those examples ready for production?
diff --git a/v2/rscg_examples_site/docs/indexRSCG.md b/v2/rscg_examples_site/docs/indexRSCG.md
index 16f185bd3..539881405 100644
--- a/v2/rscg_examples_site/docs/indexRSCG.md
+++ b/v2/rscg_examples_site/docs/indexRSCG.md
@@ -7,9 +7,9 @@ slug: /List-of-RSCG
import useBaseUrl from '@docusaurus/useBaseUrl';
-## 254 RSCG with examples in descending chronological order
+## 255 RSCG with examples in descending chronological order
-This is the list of 254 ( 16 from Microsoft) RSCG with examples
+This is the list of 255 ( 16 from Microsoft) RSCG with examples
[See by category](/docs/rscg-examples) [See as json](/exports/RSCG.json) [See as Excel](/exports/RSCG.xlsx)
@@ -20,6 +20,7 @@ This is the list of 254 ( 16 from Microsoft) RSCG with examples
| No | Name | Date | Category |
| --------- | ----- | ---- | -------- |
+|255| [KnockOff by Keith Voels ](/docs/KnockOff)|2026-02-13 => 13 February 2026 | [Tests](/docs/Categories/Tests) |
|254| [ErrorOrX by Alexander Nachtmanns ](/docs/ErrorOrX)|2026-02-02 => 02 February 2026 | [API](/docs/Categories/API) |
|253| [FastCloner by Matěj Štágl ](/docs/FastCloner)|2026-02-01 => 01 February 2026 | [Clone](/docs/Categories/Clone) |
|252| [RSCG_idempotency by Ignat Andrei ](/docs/RSCG_idempotency)|2026-01-28 => 28 January 2026 | [Idempotency](/docs/Categories/Idempotency) |
diff --git a/v2/rscg_examples_site/src/components/HomepageFeatures/index.js b/v2/rscg_examples_site/src/components/HomepageFeatures/index.js
index 7427a13c4..a4510b61c 100644
--- a/v2/rscg_examples_site/src/components/HomepageFeatures/index.js
+++ b/v2/rscg_examples_site/src/components/HomepageFeatures/index.js
@@ -4,7 +4,7 @@ import styles from './styles.module.css';
const FeatureList = [
{
-title: '254 Examples (16 from MSFT)',
+title: '255 Examples (16 from MSFT)',
Svg: require('@site/static/img/undraw_docusaurus_mountain.svg').default,
description: (
<>
diff --git a/v2/rscg_examples_site/static/exports/RSCG.json b/v2/rscg_examples_site/static/exports/RSCG.json
index abef16315..39a8a25cc 100644
--- a/v2/rscg_examples_site/static/exports/RSCG.json
+++ b/v2/rscg_examples_site/static/exports/RSCG.json
@@ -2033,6 +2033,14 @@
"Source": "https://github.com/ANcpLua/ErrorOrX",
"Category": "API",
"AddedOn": "2026-02-02T00:00:00"
+ },
+ {
+ "Name": "KnockOff",
+ "Link": "https://ignatandrei.github.io/RSCG_Examples/v2/docs/KnockOff",
+ "NuGet": "https://www.nuget.org/packages/KnockOff/",
+ "Source": "https://github.com/NeatooDotNet/KnockOff",
+ "Category": "Tests",
+ "AddedOn": "2026-02-13T00:00:00"
}
]
}
\ No newline at end of file
diff --git a/v2/rscg_examples_site/static/exports/RSCG.xlsx b/v2/rscg_examples_site/static/exports/RSCG.xlsx
index a590b312a..71ca47d9d 100644
Binary files a/v2/rscg_examples_site/static/exports/RSCG.xlsx and b/v2/rscg_examples_site/static/exports/RSCG.xlsx differ
diff --git a/v2/rscg_examples_site/static/sources/KnockOff.zip b/v2/rscg_examples_site/static/sources/KnockOff.zip
new file mode 100644
index 000000000..ff47c25a2
Binary files /dev/null and b/v2/rscg_examples_site/static/sources/KnockOff.zip differ