diff --git a/README.md b/README.md
index 24036abed..19e54beb8 100644
--- a/README.md
+++ b/README.md
@@ -1,10 +1,10 @@
-# RSCG - 285 Examples of Roslyn Source Code Generators / 16 created by Microsoft /
+# RSCG - 286 Examples of Roslyn Source Code Generators / 16 created by Microsoft /
-The RSCG_Examples repository is a comprehensive documentation system that automatically processes and showcases 285 Roslyn Source Code Generator (RSCG) examples. The system transforms individual RSCG projects into structured documentation with code examples and cross-referenced content with a searchable website and code example exports.
+The RSCG_Examples repository is a comprehensive documentation system that automatically processes and showcases 286 Roslyn Source Code Generator (RSCG) examples. The system transforms individual RSCG projects into structured documentation with code examples and cross-referenced content with a searchable website and code example exports.
This system serves as both a learning resource for .NET developers interested in source generators and an automated pipeline for maintaining up-to-date documentation about the RSCG ecosystem
-## Latest Update : 2026-08-26 => 26 August 2026
+## Latest Update : 2026-08-27 => 27 August 2026
If you want to see examples with code, please click ***[List V2](https://ignatandrei.github.io/RSCG_Examples/v2/docs/List-of-RSCG)***
@@ -24,8 +24,30 @@ If you want to be notified each time I add a new RSCG example , please click htt
## Content
-Those are the 285 Roslyn Source Code Generators that I have tested you can see and download source code example.
+Those are the 286 Roslyn Source Code Generators that I have tested you can see and download source code example.
( including 16 from Microsoft )
+### 286. [SatorImaging.TDoubles](https://ignatandrei.github.io/RSCG_Examples/v2/docs/SatorImaging.TDoubles) , in the [Tests](https://ignatandrei.github.io/RSCG_Examples/v2/docs/rscg-examples#tests) category
+
+Generated on : 2026-08-27 => 27 August 2026
+
+
+ Expand
+
+
+
+Author: Sator Imaging
+
+Incremental source generator for creating mock wrapper classes.
+
+Nuget: [https://www.nuget.org/packages/SatorImaging.TDoubles/](https://www.nuget.org/packages/SatorImaging.TDoubles/)
+
+
+Link: [https://ignatandrei.github.io/RSCG_Examples/v2/docs/SatorImaging.TDoubles](https://ignatandrei.github.io/RSCG_Examples/v2/docs/SatorImaging.TDoubles)
+
+Source: [https://github.com/sator-imaging/TDoubles](https://github.com/sator-imaging/TDoubles)
+
+
+
### 285. [PropertyResolvers](https://ignatandrei.github.io/RSCG_Examples/v2/docs/PropertyResolvers) , in the [EnhancementProject](https://ignatandrei.github.io/RSCG_Examples/v2/docs/rscg-examples#enhancementproject) category
Generated on : 2026-08-26 => 26 August 2026
diff --git a/later.md b/later.md
index f93b018c1..db0f7aed3 100644
--- a/later.md
+++ b/later.md
@@ -1,6 +1,6 @@
# Just later
-## Latest Update : 2026-08-26 => 26 August 2026
+## Latest Update : 2026-08-27 => 27 August 2026
diff --git a/v2/.tours/SatorImaging.TDoubles.tour b/v2/.tours/SatorImaging.TDoubles.tour
new file mode 100644
index 000000000..bd4c9366c
--- /dev/null
+++ b/v2/.tours/SatorImaging.TDoubles.tour
@@ -0,0 +1,48 @@
+
+{
+ "$schema": "https://aka.ms/codetour-schema",
+ "title": "SatorImaging.TDoubles",
+ "steps":
+ [
+ {
+ "file": "rscg_examples/SatorImaging.TDoubles/src/TestClock/TestClock.csproj",
+ "description": "First, we add Nuget [SatorImaging.TDoubles](https://www.nuget.org/packages/SatorImaging.TDoubles/) in csproj ",
+ "pattern": "SatorImaging.TDoubles"
+ }
+
+ ,{
+ "file": "rscg_examples/SatorImaging.TDoubles/src/TestClock/TestClock.cs",
+ "description": "File TestClock.cs ",
+ "pattern": "this is the code"
+ }
+
+ ,{
+ "file": "rscg_examples/SatorImaging.TDoubles/src/Mock/IMyClock.cs",
+ "description": "File IMyClock.cs ",
+ "pattern": "this is the code"
+ }
+
+
+ ,{
+ "file": "rscg_examples/SatorImaging.TDoubles/src/TestClock/obj/GX/SatorImaging.TDoubles/TDoubles.TDoublesSourceGenerator/_ TDoubles _ TDoublesException.g.cs",
+ "description": "Generated File 3 from 3 : _ TDoubles _ TDoublesException.g.cs ",
+ "line": 1
+ }
+
+ ,{
+ "file": "rscg_examples/SatorImaging.TDoubles/src/TestClock/obj/GX/SatorImaging.TDoubles/TDoubles.TDoublesSourceGenerator/_ TDoubles _ MockAttribute.g.cs",
+ "description": "Generated File 2 from 3 : _ TDoubles _ MockAttribute.g.cs ",
+ "line": 1
+ }
+
+ ,{
+ "file": "rscg_examples/SatorImaging.TDoubles/src/TestClock/obj/GX/SatorImaging.TDoubles/TDoubles.TDoublesSourceGenerator/TestClock.QuickStartRepoStub.g.cs",
+ "description": "Generated File 1 from 3 : TestClock.QuickStartRepoStub.g.cs ",
+ "line": 1
+ }
+
+ ],
+
+ "ref": "main"
+
+}
\ No newline at end of file
diff --git a/v2/Generator/all.csv b/v2/Generator/all.csv
index 6f6c4946f..8ed5de681 100644
--- a/v2/Generator/all.csv
+++ b/v2/Generator/all.csv
@@ -284,3 +284,4 @@ Nr,Key,Source,Category
283,IncrementalSourceGenerator.BuilderPattern, https://github.com/MartinBarrettNielsen11/Incremental-source-generator-builder-pattern,Builder
284,Pinecone.TypedPath, https://github.com/nickpinecone/typed-path,FilesToCode
285,PropertyResolvers, https://github.com/tombiddulph/PropertyResolvers,EnhancementProject
+286,SatorImaging.TDoubles, https://github.com/sator-imaging/TDoubles,Tests
diff --git a/v2/RSCGExamplesData/GeneratorDataRec.json b/v2/RSCGExamplesData/GeneratorDataRec.json
index 9fda4a89d..4e0c4b142 100644
--- a/v2/RSCGExamplesData/GeneratorDataRec.json
+++ b/v2/RSCGExamplesData/GeneratorDataRec.json
@@ -1727,6 +1727,12 @@
"Category": 1,
"dtStart": "2026-08-26T00:00:00",
"show": true
+ },
+ {
+ "ID": "SatorImaging.TDoubles",
+ "Category": 13,
+ "dtStart": "2026-08-27T00:00:00",
+ "show": true
}
]
\ No newline at end of file
diff --git a/v2/book/examples/SatorImaging.TDoubles.html b/v2/book/examples/SatorImaging.TDoubles.html
new file mode 100644
index 000000000..dbd0a58b9
--- /dev/null
+++ b/v2/book/examples/SatorImaging.TDoubles.html
@@ -0,0 +1,69 @@
+
+
RSCG nr 286 : SatorImaging.TDoubles
+
+Info
+Nuget : https://www.nuget.org/packages/SatorImaging.TDoubles/
+
+You can find more details at : https://github.com/sator-imaging/TDoubles
+
+Author :Sator Imaging
+
+Source: https://github.com/sator-imaging/TDoubles
+
+About
+
+Generating test stubs with mocking for interfaces
+
+
+ How to use
+
+
+
+
+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 TestClock.QuickStartRepoStub.g.cs
+
+
+
+
+ The file generated is _ TDoubles _ MockAttribute.g.cs
+
+
+
+
+ The file generated is _ TDoubles _ TDoublesException.g.cs
+
+
+
+
+ You can download the code and this page as pdf from
+
+ https://ignatandrei.github.io/RSCG_Examples/v2/docs/SatorImaging.TDoubles
+
+
+
+
+
+ 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 717346c51..158b1c9f2 100644
--- a/v2/book/list.html
+++ b/v2/book/list.html
@@ -17,7 +17,7 @@
-This is the list of 285 RSCG with examples =>
+This is the list of 286 RSCG with examples =>
diff --git a/v2/book/pandocHTML.yaml b/v2/book/pandocHTML.yaml
index d6de7a8d7..ebec810a2 100644
--- a/v2/book/pandocHTML.yaml
+++ b/v2/book/pandocHTML.yaml
@@ -299,6 +299,7 @@ input-files:
- examples/IncrementalSourceGenerator.BuilderPattern.html
- examples/Pinecone.TypedPath.html
- examples/PropertyResolvers.html
+- examples/SatorImaging.TDoubles.html
# or you may use input-file: with a single value
# defaults:
diff --git a/v2/docFind.json b/v2/docFind.json
index 82e178172..4f3c3ee43 100644
--- a/v2/docFind.json
+++ b/v2/docFind.json
@@ -1707,6 +1707,12 @@
"title": "PropertyResolvers",
"category": "EnhancementProject",
"href": "/RSCG_Examples/v2/docs/PropertyResolvers/",
- "body": "A source generator that creates property resolver classes for types with\n matching properties. Use assembly-level attributes to generate resolvers that can\n retrieve property values from objects at runtime."
+ "body": "A source generator that creates property resolver classes for types with\r\n matching properties. Use assembly-level attributes to generate resolvers that can\r\n retrieve property values from objects at runtime."
+ },
+ {
+ "title": "SatorImaging.TDoubles",
+ "category": "Tests",
+ "href": "/RSCG_Examples/v2/docs/SatorImaging.TDoubles/",
+ "body": "Incremental source generator for creating mock wrapper classes."
}
]
\ No newline at end of file
diff --git a/v2/rscg_examples/SatorImaging.TDoubles/description.json b/v2/rscg_examples/SatorImaging.TDoubles/description.json
new file mode 100644
index 000000000..3657d81d4
--- /dev/null
+++ b/v2/rscg_examples/SatorImaging.TDoubles/description.json
@@ -0,0 +1,22 @@
+{
+ "generator":{
+ "name":"SatorImaging.TDoubles",
+ "nuget":[
+ "https://www.nuget.org/packages/SatorImaging.TDoubles/"
+ ],
+ "link":"https://github.com/sator-imaging/TDoubles",
+ "author":"Sator Imaging",
+ "source":"https://github.com/sator-imaging/TDoubles"
+ },
+ "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/SatorImaging.TDoubles/nuget.txt b/v2/rscg_examples/SatorImaging.TDoubles/nuget.txt
new file mode 100644
index 000000000..e752659fd
--- /dev/null
+++ b/v2/rscg_examples/SatorImaging.TDoubles/nuget.txt
@@ -0,0 +1 @@
+Incremental source generator for creating mock wrapper classes.
\ No newline at end of file
diff --git a/v2/rscg_examples/SatorImaging.TDoubles/readme.txt b/v2/rscg_examples/SatorImaging.TDoubles/readme.txt
new file mode 100644
index 000000000..c43f0cab6
--- /dev/null
+++ b/v2/rscg_examples/SatorImaging.TDoubles/readme.txt
@@ -0,0 +1,839 @@
+[](https://www.nuget.org/packages/SatorImaging.TDoubles)
+[](https://github.com/sator-imaging/TDoubles/actions/workflows/test.yml)
+
+[](https://deepwiki.com/sator-imaging/TDoubles)
+
+[๐บ๐ธ English](./README.md)
+ โ
+[๐ฏ๐ต ๆฅๆฌ่ช็](./README.ja.md)
+ โ
+[๐จ๐ณ ็ฎไฝไธญๆ็](./README.zh-CN.md)
+
+
+
+
+
+
+
+`TDoubles` is a powerful C# source generator that revolutionizes unit testing by creating mock wrapper classes at compile-time. Instead of relying on complex runtime reflection or proxy generation like traditional mocking frameworks, this generator produces clean, readable C# code during compilation that wraps your target types with customizable behavior.
+
+
+
+## โจ Comparison with Traditional Mocking Frameworks
+
+| Feature | TDoubles | Traditional Frameworks (Moq, NSubstitute) |
+|---------|---------------------------|-------------------------------------------|
+| **Performance** | Zero runtime overhead, compile-time generation | Runtime reflection and proxy creation |
+| **Type Safety** | Full compile-time checking and IntelliSense | Runtime configuration, limited IntelliSense |
+| **Generic Support** | Full support including constraints | Limited generic type support |
+| **Setup Complexity** | Single attribute, minimal configuration | Complex fluent APIs and setup expressions |
+| **Debugging** | Generated code is readable and debuggable | Proxy objects can be difficult to debug |
+
+
+
+
+
+# โก Quick Start
+
+Apply the `[Mock]` attribute to a partial class, and the generator handles the rest.
+
+```cs
+using TDoubles;
+
+public interface IDataService
+{
+ string GetData(int id);
+ void SaveData(string data);
+}
+
+[Mock(typeof(IDataService))] // ๐
+partial class DataServiceMock
+{
+ // Implementation will be generated automatically
+}
+```
+
+
+Here shows how to use the mock in your code.
+
+```cs
+// Create the mock
+var mockService = new DataServiceMock();
+
+// Override behavior for testing
+mockService.MockOverrides.GetData = (id) => $"MockData_{id}";
+// ~~~~~~~~~~~~~
+
+string mockData = mockService.GetData(123); // Returns "MockData_123"
+```
+
+
+You can delegate to real implementation and override partial behaviour of the mock.
+
+```cs
+var mock = new DataServiceMock(new ConcreteDataService());
+
+// Use default behavior (delegates to real service)
+var realData = mock.GetData(123);
+
+// Override partial behaviour for testing
+mock.MockOverrides.SaveData = (data) => Console.WriteLine($"Saved: {data}");
+mock.SaveData(realData);
+```
+
+
+Implements fake behaviors for debugging purposes in conjunction with latest update to the real implementation.
+
+```cs
+[Mock(typeof(IFoo), nameof(IFoo.Save), nameof(IFoo.Load))]
+partial class FooFake
+{
+ public void Save() => File.WriteAllText("...", JsonUtility.ToJson(this, true));
+ public void Load() => JsonUtility.FromJsonOverwrite(File.ReadAllText("..."), this);
+}
+
+// Delegates to latest ConcreteFoo implementation except for Save and Load
+var fake = new FooFake(new ConcreteFoo());
+```
+
+
+
+
+## Generic Type Support
+
+`TDoubles` provides support for generic type mocking on both unbound and closed constructed generics.
+
+```cs
+[Mock(typeof(IList))]
+partial class ListIntMock {}
+
+// Proper type constraint for TKey is automatically generated and
+// type parameter naming mismatch is also resolved
+[Mock(typeof(IDictioanry<,>))]
+partial class DictionaryMock {}
+```
+
+
+## Efficient Callback Support
+
+There are efficient extension points to implement custom callback for each mock member call.
+
+> [!TIP]
+> As C# specification, `partial void` method call is completely removed from built assembly when method body is not implemented in your mock class declaration.
+>
+> https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/partial-member
+
+```cs
+[Mock(typeof(IList<>))]
+partial class ListSpy // ๐ต < Investigate suspects!
+{
+ readonly Dictionary _callCountByName = new();
+
+ // Without allocating object?[] instance
+ partial void OnWillMockCall(string memberName)
+ {
+ if (!_callCountByName.TryGetValue(memberName, out var current))
+ {
+ current = 0;
+ }
+ _callCountByName[memberName] = current + 1;
+ }
+
+ // Another overload can take arguments passed to mock member
+ // * Array.Empty() is used for parameterless members
+ partial void OnWillMockCall(string memberName, object?[] args)
+ {
+ // How to determine method overload
+ if (memberName == "Add")
+ {
+ if (args[0] is T)
+ {
+ Console.WriteLine("Add(T item) is invoked.");
+ }
+ else
+ {
+ Console.WriteLine("Add(object item) is invoked.");
+ }
+ }
+ }
+}
+```
+
+
+## `Mock` Attribute Options
+
+There are options to select generated mock members.
+
+```cs
+// Include internal types, interfaces and members to mock generation
+[Mock(typeof(Foo), IncludeInternals = true)]
+partial class FooMock { }
+
+// Exclude specified members from mock generation (no error if member is not found)
+[Mock(typeof(Foo), "ToString", "Foo", "Bar", IncludeInternals = false)]
+partial class FooMockWithoutToStringOverride
+{
+ // You can re-implement excluded 'ToString' as you desired
+ public override string ToString() => base.ToString() ?? "";
+}
+```
+
+
+
+
+
+# Introduction
+
+The generator works by analyzing types marked with the `[Mock]` attribute and generating corresponding mock classes that delegate to the original implementation while providing override capabilities through a simple, strongly-typed API. This approach eliminates the performance overhead of reflection-based mocking while maintaining full type safety and IntelliSense support.
+
+
+## Key Benefits
+
+- **Zero Runtime Overhead**: Mock classes are generated at compile-time, eliminating reflection costs and improving test execution performance
+- **Full Type Safety**: Generated mocks provide complete IntelliSense support, compile-time checking, and refactoring safety
+- **Minimal Setup**: Simply add the NuGet package, apply the `[Mock]` attribute to a partial class, and the generator handles the rest
+- **Universal Compatibility**: Supports all major C# type constructs including interfaces, classes, records, record structs, regular structs, and static classes
+- **Advanced Generic Support**: Handles complex generic scenarios including type constraints, nested generics, and generic method overloads
+- **Internal Member Access**: Optional `IncludeInternals` configuration allows mocking of internal members for comprehensive testing
+- **Clean Generated Code**: Produces human-readable, debuggable mock implementations that integrate seamlessly with your codebase
+
+
+## Use Cases
+
+TDoubles generator excels in scenarios where you need:
+
+- **High-Performance Testing**: When test execution speed is critical and reflection overhead is unacceptable
+- **Legacy Code Testing**: Mocking existing classes and structs that weren't designed with interfaces
+- **Static Method Testing**: Converting static methods to testable instance methods through mock wrappers
+- **Record and Struct Mocking**: Testing value types and immutable records that traditional frameworks struggle with
+- **Complex Generic Testing**: Mocking generic types with multiple type parameters and constraints
+- **Internal API Testing**: Testing internal members without making them public
+
+
+## How It Works
+
+1. **Mark Target Types**: Apply the `[Mock(typeof(TargetType))]` attribute to a partial class
+2. **Compile-Time Generation**: The source generator analyzes your target type and creates a mock implementation
+3. **Delegate with Overrides**: Generated mocks delegate to the original instance while providing `MockOverrides` for custom behavior
+4. **Test with Confidence**: Use the generated mock in your tests with full type safety and performance
+
+### Delegation Logic
+
+Here is pseudo code of delegation. Actual code is more complicated as need to support `ref` and `out` parameter modifiers.
+
+```cs
+public string GetData(int id)
+{
+ // Returns 'default' if value type or nullable reference type otherwise throws
+ return MockOverrides.GetData?.Invoke(id)
+ ?? _target?.GetData(id)
+ ?? throw new TDoublesException(...);
+}
+```
+
+### Generated Mock Structure
+
+When you create a mock class, the generator adds several members:
+
+```csharp
+[Mock(typeof(IUserService))]
+partial class UserServiceMock
+{
+ // Generated by source generator:
+
+ // Constructor that takes the target instance
+ public UserServiceMock(IUserService? target = default) { }
+
+ // Access to the underlying target
+ public IUserService? MockTarget { get; }
+
+ // Unified callback
+ partial void OnWillMockCall(string memberName);
+ partial void OnWillMockCall(string memberName, object?[] args);
+
+ // Override configuration object
+ public sealed partial class MockOverrideContainer { }
+ public MockOverrideContainer MockOverrides { get; }
+
+ // All interface/class members are implemented
+ public string GetUserName(int userId) { /* generated implementation */ }
+ public Task DeleteUser(int userId) { /* generated implementation */ }
+ // ... etc
+}
+```
+
+
+
+
+
+# Installation
+
+## NuGet Package Installation
+
+### Package Manager Console
+
+```powershell
+Install-Package SatorImaging.TDoubles
+```
+
+### .NET CLI
+
+```bash
+dotnet add package SatorImaging.TDoubles
+```
+
+### PackageReference (Manual)
+
+Add the following to your project file (`.csproj`):
+
+```xml
+
+```
+
+
+## System Requirements
+
+- **.NET Framework**: .NET Standard 2.0 or higher
+- **C# Language Version**: C# 7.3 or later
+- **Compatible Runtimes**:
+ - .NET Framework 4.6.1+
+ - .NET Core 2.0+
+ - .NET 5.0+
+ - Unity 2022.3.12f1 or later
+
+
+## Setup and Configuration
+
+### Basic Setup
+
+1. Install the NuGet package using one of the methods above
+2. Rebuild your project to enable the source generator
+3. Create partial classes with the `[Mock]` attribute to generate mocks
+
+### Project Configuration
+
+No additional project configuration is required. The source generator automatically activates when the package is installed and will generate mock classes during compilation.
+
+### Verification
+
+To verify the installation was successful:
+
+1. Add a simple mock class to your project:
+ ```csharp
+ using TDoubles;
+
+ public interface ITestService
+ {
+ string GetMessage();
+ }
+
+ [Mock(typeof(ITestService))]
+ partial class TestServiceMock
+ {
+ // Mock implementation will be generated here
+ }
+ ```
+2. Build your project
+3. Check that no compilation errors occur and the mock class is generated
+
+
+### IDE Support
+- **Visual Studio**: Full IntelliSense support for generated mock classes
+- **Visual Studio Code**: Works with C# extension
+- **JetBrains Rider**: Full support with code completion
+- **Command Line**: Works with `dotnet build` and `msbuild`
+
+
+
+
+
+# Basic Usage
+
+This section provides step-by-step examples to get you started with `TDoubles`. All examples are complete and ready to use in your projects.
+
+
+## Prerequisites
+
+Before using the TDoubles generator, ensure your mock classes meet these requirements:
+
+1. **Partial Class**: Mock classes must be declared as `partial`
+2. **Mock Attribute**: Apply `[Mock(typeof(TargetType))]` to the partial class
+3. **Namespace**: Include `using TDoubles;`
+4. **Visibility**: Use any visibility modifier (public, internal, etc.) - generated members will inherit the same visibility
+
+
+## Simple Interface Mocking
+
+The most common scenario is mocking interfaces for dependency injection testing.
+
+### Example: User Service Interface
+
+```csharp
+using TDoubles;
+using System;
+using System.Threading.Tasks;
+
+// Define your interface
+public interface IUserService
+{
+ string GetUserName(int userId);
+ Task DeleteUser(int userId);
+ bool IsUserActive(int userId);
+}
+
+// Create a partial mock class
+[Mock(typeof(IUserService))]
+partial class UserServiceMock
+{
+ // The source generator will create the complete implementation here
+}
+
+// Example usage in tests
+class Program
+{
+ static void Main()
+ {
+ // Create a concrete implementation for delegation
+ var realService = new ConcreteUserService();
+
+ // Create the mock with the real service as the underlying target
+ var mockService = new UserServiceMock(realService);
+
+ Console.WriteLine("=== Default Behavior (Delegates to Real Service) ===");
+ Console.WriteLine($"User Name: {mockService.GetUserName(123)}");
+ Console.WriteLine($"Is Active: {mockService.IsUserActive(123)}");
+
+ Console.WriteLine("\n=== Custom Behavior with Overrides ===");
+
+ // Override specific methods for testing
+ mockService.MockOverrides.GetUserName = (userId) => $"MockUser_{userId}";
+ mockService.MockOverrides.IsUserActive = (userId) => userId > 100;
+
+ Console.WriteLine($"User Name (Overridden): {mockService.GetUserName(123)}");
+ Console.WriteLine($"Is Active (Overridden): {mockService.IsUserActive(50)}");
+ Console.WriteLine($"Is Active (Overridden): {mockService.IsUserActive(150)}");
+
+ // Access the underlying real service if needed
+ Console.WriteLine($"Real Service: {mockService.MockTarget.GetUserName(123)}");
+ }
+}
+
+// Concrete implementation for demonstration
+public class ConcreteUserService : IUserService
+{
+ public string GetUserName(int userId) => $"RealUser_{userId}";
+ public async Task DeleteUser(int userId) => await Task.FromResult(true);
+ public bool IsUserActive(int userId) => true;
+}
+```
+
+
+## Class Mocking with Inheritance
+
+Mock concrete classes to test inheritance scenarios and virtual method overrides.
+
+### Example: Service Class with Virtual Methods
+
+```csharp
+using TDoubles;
+using System;
+
+// Base service class with virtual methods
+public class DatabaseService
+{
+ public virtual string GetConnectionString() => "Server=localhost;Database=prod;";
+ public virtual void SaveData(string data) => Console.WriteLine($"Saving to database: {data}");
+ public virtual int GetRecordCount() => 1000;
+
+ // Non-virtual method (will be wrapped but not overridable)
+ public string GetServiceName() => "DatabaseService";
+}
+
+// Create mock for the class
+[Mock(typeof(DatabaseService))]
+partial class DatabaseServiceMock
+{
+ // Generated implementation will wrap all public methods
+}
+
+// Example usage
+class Program
+{
+ static void Main()
+ {
+ // Create real service instance
+ var realService = new DatabaseService();
+
+ // Create mock wrapper
+ var mockService = new DatabaseServiceMock(realService);
+
+ Console.WriteLine("=== Default Behavior ===");
+ Console.WriteLine($"Connection: {mockService.GetConnectionString()}");
+ Console.WriteLine($"Service Name: {mockService.GetServiceName()}");
+ Console.WriteLine($"Record Count: {mockService.GetRecordCount()}");
+ mockService.SaveData("test data");
+
+ Console.WriteLine("\n=== Testing Scenario Overrides ===");
+
+ // Override for testing scenarios
+ mockService.MockOverrides.GetConnectionString = () => "Server=testserver;Database=test;";
+ mockService.MockOverrides.GetRecordCount = () => 0; // Simulate empty database
+ mockService.MockOverrides.SaveData = (data) => Console.WriteLine($"TEST MODE: Would save '{data}'");
+
+ Console.WriteLine($"Test Connection: {mockService.GetConnectionString()}");
+ Console.WriteLine($"Test Record Count: {mockService.GetRecordCount()}");
+ mockService.SaveData("test data");
+
+ // Non-virtual methods still work but delegate to original
+ Console.WriteLine($"Service Name (always delegates): {mockService.GetServiceName()}");
+ }
+}
+```
+
+
+## Inheritance and Interface Implementation
+
+Mock classes that both inherit from base classes and implement interfaces.
+
+### Example: Complex Service Hierarchy
+
+```csharp
+using TDoubles;
+using System;
+
+// Interface definition
+public interface INotificationService
+{
+ void SendNotification(string message);
+ bool IsServiceAvailable();
+}
+
+// Base class with virtual methods
+public class BaseService
+{
+ public virtual string GetServiceType() => "Base";
+ public virtual void Initialize() => Console.WriteLine("Base initialization");
+}
+
+// Concrete class that inherits and implements interface
+public class EmailService : BaseService, INotificationService
+{
+ public override string GetServiceType() => "Email";
+ public override void Initialize() => Console.WriteLine("Email service initialized");
+
+ public void SendNotification(string message) => Console.WriteLine($"Email: {message}");
+ public bool IsServiceAvailable() => true;
+}
+
+// Mock the concrete class
+[Mock(typeof(EmailService))]
+partial class EmailServiceMock
+{
+ // Mocks both inherited methods and interface implementations
+}
+
+// Usage example
+class Program
+{
+ static void Main()
+ {
+ var realService = new EmailService();
+ var mockService = new EmailServiceMock(realService);
+
+ Console.WriteLine("=== Testing Inherited Methods ===");
+ Console.WriteLine($"Service Type: {mockService.GetServiceType()}");
+ mockService.Initialize();
+
+ Console.WriteLine("\n=== Testing Interface Methods ===");
+ mockService.SendNotification("Hello World");
+ Console.WriteLine($"Available: {mockService.IsServiceAvailable()}");
+
+ Console.WriteLine("\n=== Testing with Overrides ===");
+
+ // Override inherited method
+ mockService.MockOverrides.GetServiceType = () => "MockEmail";
+ mockService.MockOverrides.Initialize = () => Console.WriteLine("Mock initialization");
+
+ // Override interface methods
+ mockService.MockOverrides.SendNotification = (msg) => Console.WriteLine($"MOCK EMAIL: {msg}");
+ mockService.MockOverrides.IsServiceAvailable = () => false;
+
+ Console.WriteLine($"Service Type: {mockService.GetServiceType()}");
+ mockService.Initialize();
+ mockService.SendNotification("Test Message");
+ Console.WriteLine($"Available: {mockService.IsServiceAvailable()}");
+ }
+}
+```
+
+
+
+
+
+# Advanced Usage
+
+For advanced scenarios including generic types, static classes, records, structs, and internal member access, see the [Advanced Usage Guide](docs/advanced-usage.md).
+
+
+
+
+
+# Testing Examples
+
+For comprehensive testing examples with MSTest, NUnit, and performance comparisons, see the [Testing Examples Guide](docs/testing-examples.md).
+
+
+
+
+
+
+
+
+
+
+# Technical Note
+
+## `record` and `record struct`
+
+- Always implements `IEquatable` and `MockOverrides.MockTargetRecord_Equals`
+ - Note that it is *NOT* `IEquatable`
+- `bool Equals(object?)` cannot be overridden
+
+
+
+
+
+# Known Limitations and Unsupported Scenarios
+
+## Type Parameters of Generic Method
+
+When method uses method-level type parameter instead of type-level parameter, `MockOverrides` will use `object` instead of method-level type parameter.
+
+```cs
+// Generated mock has type-level parameter T
+partial class Mock
+{
+ // Generated mock method that has T and TMethod type parameter
+ public TMethod GenericMethod(T input) { ... }
+
+ // can be added to this class but it must also be exposed as type-level parameter...
+ public sealed partial class MockOverrideContainer
+ {
+ // type-level parameter T is used but TMethod is shadowed to object
+ public Func GenericMethod { get; set; }
+ // ~~~~~~ Not TMethod
+ }
+}
+```
+
+> [!NOTE]
+> Generated mock method returns `TMethod` as mock target does. Internally, mock method will cast `object` result from override to `TMethod` when returning value.
+
+
+## Type System Limitations
+
+**Unsupported Types:**
+- Enums (use wrapper classes instead)
+- Delegates and function pointers
+- Primitive types (`int`, `string`, etc.)
+- Static classes with only static constructors
+- Abstract classes with pure virtual methods requiring implementation
+- `object`, `ValueType`, `Enum` and other special types such as `Span`
+
+
+## Type Constraint Limitations
+
+**Unsupported Constraints:**
+- `where T : default`
+- `where T : allows ref struct`
+
+
+## Return Type Limitations
+
+**Unsupported Type:**
+- `ref` return type
+
+
+## Attribute Limitations
+
+Attributes on type, method, property or etc are not preserved in generated mock.
+
+
+## Method and Property Limitations
+
+**Unsupported Members:**
+- `ref` and `out` parameters in some complex scenarios (?)
+- ~~Methods with `__arglist` (variable arguments)~~
+- ~~Explicit interface implementations with name conflicts~~
+- ~~Properties with complex getter/setter accessibility combinations~~
+
+**Partial Support:**
+
+```csharp
+public interface IService
+{
+ // โ
Fully supported
+ string GetData(int id);
+ Task ProcessAsync(string data);
+
+ // โ ๏ธ Limited support - may not override correctly
+ ref int GetReference();
+ void ProcessData(__arglist);
+}
+```
+
+
+## Generic Method Limitations
+
+Some valid type constraint is not transformed correctly. We have no plan to support this edge case of type constraint.
+
+> Note: `override` method cannot have type constraint except for `class` and `struct`.
+
+```cs
+// Abstract method declaration that returns (M, N?) with where M : N? constraint
+public abstract (M t, N? u) TypeArgMappingNullable_Abstract() where M : N?;
+
+// Expected (valid) return type is (M, N)
+public override (M t, N u) TypeArgMappingNullable_Abstract() { }
+
+// But got (M, N?)
+public override (M t, N? u) TypeArgMappingNullable_Abstract() { }
+```
+
+
+
+## Inheritance and Interface Limitations
+
+**Multiple Interface Implementation:**
+- ~~Supported, but explicit interface implementations may have naming conflicts~~
+- Diamond inheritance patterns may cause method resolution issues
+
+**Virtual Method Overriding:**
+- ~~Only `virtual` and `abstract` methods can be overridden in class mocks~~
+- `sealed` methods cannot be overridden (will delegate to original)
+
+
+## Platform and Framework Limitations
+
+**Framework Support:**
+- Requires .NET Standard 2.0 or higher
+- Source generators require C# 7.3 or later
+- Some advanced C# 11+ features may not be fully supported
+
+**IDE Integration:**
+- IntelliSense may be delayed for newly generated mocks
+- Some IDEs may require rebuild to recognize generated code
+- Debugging generated code may show optimized/synthetic code
+
+
+
+
+
+# Contributing
+
+We welcome and appreciate contributions from the community! Whether you're fixing bugs, adding features, improving documentation, or providing feedback, your contributions help make TDoubles better for everyone.
+
+See [CONTRIBUTING.md](CONTRIBUTING.md)
+
+
+
+
+
+# Code of Conduct
+
+We are committed to providing a welcoming and inclusive environment for all contributors. Please be respectful and professional in all interactions.
+
+
+
+
+
+# Support and Community
+
+## Getting Help
+
+If you encounter issues not covered in this troubleshooting guide:
+
+1. **Check the GitHub Issues:** Search existing issues for similar problems
+2. **Create a Minimal Reproduction:** Provide the smallest possible code example that demonstrates the issue
+3. **Include Build Output:** Share relevant compiler errors and warnings
+4. **Specify Environment:** Include .NET version, IDE, and operating system details
+
+**Support Channels:**
+- [GitHub Discussions](https://github.com/sator-imaging/TDoubles/discussions) - Questions and community support
+- [GitHub Issues](https://github.com/sator-imaging/TDoubles/issues) - Bug reports and feature requests
+
+
+## Reporting Security Issues
+
+If you discover a security vulnerability, please report it privately by emailing the maintainers rather than creating a public issue. This allows us to address the issue before it becomes widely known.
+
+
+
+
+
+# Project Information
+
+## TODO: Help Wanted
+
+- Missing Tests
+ - `static` class mocking
+ - `sealed` overridden methods
+ - `async` tests
+ - `event` getter and setter tests
+ - `readonly struct` tests
+ - `readonly record struct` tests
+ - `Tuple` and `ValueTuple` tests
+ - Property and indexer accessibility tests (e.g., `{ get; private set; }` or etc)
+- Missing Features
+ - `ref` return
+ - Attribute preservation
+ - Add proper ` ` for mock members
+ - Support for `default` and `allows ref struct` type constraint
+ - The `default` constraint is valid on override and explicit interface implementation methods only
+ - Roslyn update is required while keeping Unity engine support
+ - Emit diagnostic error on type parameters
+- Optimization
+ - Use `ImmutableArray` or `ImmutableList` as possible
+ - Eliminate inefficient `StringBuilder` use
+- Refactor
+ - Eliminate FP programming techniques
+ - Transform data model to domain model to encapsulate information and behaviour
+ - Centralize blueprint-to-C# conversion in domain model to make it consistent, robust and maintainable
+ - Eliminate duplicate functions, control flows and etc scattered in codebase
+- Optional
+ - ~~New `Mock` attribute option to generate `MockCallCounts` that records the call count of each mock member~~
+ - Declare `volatile int` fields
+ - Increment count by `Interlocked.Increment(ref ...)` method at the beginning of generated mock class member.
+
+
+## Author and Maintainer
+
+**Sator Imaging**
+- GitHub: [@sator-imaging](https://github.com/sator-imaging)
+- Project Repository: [sator-imaging/TDoubles](https://github.com/sator-imaging/TDoubles)
+
+
+## Acknowledgments
+
+We thank all contributors who have helped improve this project through code contributions, bug reports, feature suggestions, and community support.
+
+
+## License
+
+This project is licensed under the **MIT License**.
+
+### Third-Party Licenses
+
+This project uses the following third-party packages:
+- **Microsoft.CodeAnalysis.CSharp** (MIT License)
+- **Microsoft.CodeAnalysis.Analyzers** (MIT License)
+
+
+
+
+
+---
+
+**© 2025 Sator Imaging. All rights reserved.**
+
+For support, questions, or contributions, please visit our [GitHub repository](https://github.com/sator-imaging/TDoubles).
diff --git a/v2/rscg_examples/SatorImaging.TDoubles/src/Mock/IMyClock.cs b/v2/rscg_examples/SatorImaging.TDoubles/src/Mock/IMyClock.cs
new file mode 100644
index 000000000..b5f9f222d
--- /dev/null
+++ b/v2/rscg_examples/SatorImaging.TDoubles/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/SatorImaging.TDoubles/src/Mock/MockData.csproj b/v2/rscg_examples/SatorImaging.TDoubles/src/Mock/MockData.csproj
new file mode 100644
index 000000000..9ed914b5b
--- /dev/null
+++ b/v2/rscg_examples/SatorImaging.TDoubles/src/Mock/MockData.csproj
@@ -0,0 +1,9 @@
+
+
+
+ net10.0
+ enable
+ enable
+
+
+
diff --git a/v2/rscg_examples/SatorImaging.TDoubles/src/MockRock.slnx b/v2/rscg_examples/SatorImaging.TDoubles/src/MockRock.slnx
new file mode 100644
index 000000000..e2f5c864b
--- /dev/null
+++ b/v2/rscg_examples/SatorImaging.TDoubles/src/MockRock.slnx
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/v2/rscg_examples/SatorImaging.TDoubles/src/TestClock/TestClock.cs b/v2/rscg_examples/SatorImaging.TDoubles/src/TestClock/TestClock.cs
new file mode 100644
index 000000000..b995b76b9
--- /dev/null
+++ b/v2/rscg_examples/SatorImaging.TDoubles/src/TestClock/TestClock.cs
@@ -0,0 +1,25 @@
+
+
+namespace TestClock;
+
+[TDoubles.Mock(typeof(IMyClock))]
+public partial class QuickStartRepoStub { }
+
+
+[TestClass]
+public class TestClock
+{
+ [TestMethod]
+ public void TestMyClock()
+ {
+ var expectations = new QuickStartRepoStub();
+ expectations.MockOverrides.GetNow =()=>(DateTime.Now.AddYears(-1));
+
+ IMyClock mock = expectations;
+ var data= mock.GetNow();
+ Assert.AreEqual(DateTime.Now.Year -1, data.Year);
+
+ }
+}
+
+
diff --git a/v2/rscg_examples/SatorImaging.TDoubles/src/TestClock/TestClock.csproj b/v2/rscg_examples/SatorImaging.TDoubles/src/TestClock/TestClock.csproj
new file mode 100644
index 000000000..43af4a47d
--- /dev/null
+++ b/v2/rscg_examples/SatorImaging.TDoubles/src/TestClock/TestClock.csproj
@@ -0,0 +1,31 @@
+
+
+
+ net10.0
+ enable
+ enable
+
+ false
+ true
+
+
+
+
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
+
+
+
+ true
+ $(BaseIntermediateOutputPath)\GX
+
+
+
diff --git a/v2/rscg_examples/SatorImaging.TDoubles/src/TestClock/Usings.cs b/v2/rscg_examples/SatorImaging.TDoubles/src/TestClock/Usings.cs
new file mode 100644
index 000000000..f3be1f99c
--- /dev/null
+++ b/v2/rscg_examples/SatorImaging.TDoubles/src/TestClock/Usings.cs
@@ -0,0 +1,2 @@
+global using Microsoft.VisualStudio.TestTools.UnitTesting;
+global using MockData;
diff --git a/v2/rscg_examples/SatorImaging.TDoubles/video.json b/v2/rscg_examples/SatorImaging.TDoubles/video.json
new file mode 100644
index 000000000..25248a826
--- /dev/null
+++ b/v2/rscg_examples/SatorImaging.TDoubles/video.json
@@ -0,0 +1,39 @@
+{
+ "scriptName": "SatorImaging.TDoubles",
+ "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 SatorImaging.TDoubles . Generating test stubs with mocking for interfaces ."},
+{"typeStep":"browser","arg":"https://www.nuget.org/packages/SatorImaging.TDoubles/"},
+{"typeStep":"text","arg": "The whole example is here"},
+{"typeStep":"browser","arg":"https://ignatandrei.github.io/RSCG_Examples/v2/docs/SatorImaging.TDoubles"},
+{"typeStep":"text","arg": "You can download the code from here"},
+{"typeStep":"browser","arg":"https://ignatandrei.github.io/RSCG_Examples/v2/docs/SatorImaging.TDoubles#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 SatorImaging.TDoubles into the csproj "},
+
+{"typeStep":"stepvscode","arg": "-r -g D:\\gth\\RSCG_Examples\\v2\\rscg_examples\\SatorImaging.TDoubles\\src\\TestClock\\TestClock.csproj"},
+
+{"typeStep":"text","arg": "And now I will show you an example of using SatorImaging.TDoubles"},
+
+{"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/SatorImaging.TDoubles#download-example-net--c-",
+SpeakTest=" "},
+{"typeStep":"waitseconds","arg":"30"},
+]
+}
diff --git a/v2/rscg_examples_site/docs/Authors/Sator_Imaging.md b/v2/rscg_examples_site/docs/Authors/Sator_Imaging.md
new file mode 100644
index 000000000..515dcba79
--- /dev/null
+++ b/v2/rscg_examples_site/docs/Authors/Sator_Imaging.md
@@ -0,0 +1,7 @@
+# Author : Sator Imaging
+
+Number RSCG: 1
+
+
+ 1 [SatorImaging.TDoubles](/docs/SatorImaging.TDoubles) [](https://www.nuget.org/packages/SatorImaging.TDoubles/)  2026-08-27
+
diff --git a/v2/rscg_examples_site/docs/Categories/Tests.md b/v2/rscg_examples_site/docs/Categories/Tests.md
index 8e3d45901..b73e6b25b 100644
--- a/v2/rscg_examples_site/docs/Categories/Tests.md
+++ b/v2/rscg_examples_site/docs/Categories/Tests.md
@@ -1,6 +1,6 @@
Tests
-Number RSCG: 9
+Number RSCG: 10
1 [Imposter](/docs/Imposter) [](https://www.nuget.org/packages/Imposter/)  2025-12-13
@@ -16,7 +16,9 @@ Number RSCG: 9
7 [Rocks](/docs/Rocks) [](https://www.nuget.org/packages/Rocks/)  2023-04-16
- 8 [TinyBDD.MSTest](/docs/TinyBDD.MSTest) [](https://www.nuget.org/packages/TinyBDD.MSTest/)  2026-07-03
+ 8 [SatorImaging.TDoubles](/docs/SatorImaging.TDoubles) [](https://www.nuget.org/packages/SatorImaging.TDoubles/)  2026-08-27
- 9 [TUnit](/docs/TUnit) [](https://www.nuget.org/packages/TUnit/)  2025-11-08
+ 9 [TinyBDD.MSTest](/docs/TinyBDD.MSTest) [](https://www.nuget.org/packages/TinyBDD.MSTest/)  2026-07-03
+
+ 10 [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 40d8904ee..ca7a074f9 100644
--- a/v2/rscg_examples_site/docs/Categories/_PrimitiveTests.mdx
+++ b/v2/rscg_examples_site/docs/Categories/_PrimitiveTests.mdx
@@ -14,9 +14,11 @@
7 [Rocks](/docs/Rocks) [](https://www.nuget.org/packages/Rocks/)  2023-04-16
- 8 [TinyBDD.MSTest](/docs/TinyBDD.MSTest) [](https://www.nuget.org/packages/TinyBDD.MSTest/)  2026-07-03
+ 8 [SatorImaging.TDoubles](/docs/SatorImaging.TDoubles) [](https://www.nuget.org/packages/SatorImaging.TDoubles/)  2026-08-27
- 9 [TUnit](/docs/TUnit) [](https://www.nuget.org/packages/TUnit/)  2025-11-08
+ 9 [TinyBDD.MSTest](/docs/TinyBDD.MSTest) [](https://www.nuget.org/packages/TinyBDD.MSTest/)  2026-07-03
+
+ 10 [TUnit](/docs/TUnit) [](https://www.nuget.org/packages/TUnit/)  2025-11-08
### See category
diff --git a/v2/rscg_examples_site/docs/RSCG-Examples/SatorImaging.TDoubles.md b/v2/rscg_examples_site/docs/RSCG-Examples/SatorImaging.TDoubles.md
new file mode 100644
index 000000000..ffa86267f
--- /dev/null
+++ b/v2/rscg_examples_site/docs/RSCG-Examples/SatorImaging.TDoubles.md
@@ -0,0 +1,1265 @@
+---
+sidebar_position: 2860
+title: 286 - SatorImaging.TDoubles
+description: Generating test stubs with mocking for interfaces
+slug: /SatorImaging.TDoubles
+---
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+import TOCInline from '@theme/TOCInline';
+import SameCategory from '../Categories/_PrimitiveTests.mdx';
+
+# SatorImaging.TDoubles by Sator Imaging
+
+
+
+
+## NuGet / site data
+[](https://www.nuget.org/packages/SatorImaging.TDoubles/)
+[](https://github.com/sator-imaging/TDoubles)
+
+
+## Details
+
+### Info
+:::info
+
+Name: **SatorImaging.TDoubles**
+
+Incremental source generator for creating mock wrapper classes.
+
+Author: Sator Imaging
+
+NuGet:
+*https://www.nuget.org/packages/SatorImaging.TDoubles/*
+
+
+You can find more details at https://github.com/sator-imaging/TDoubles
+
+Source: https://github.com/sator-imaging/TDoubles
+
+:::
+
+### Author
+:::note
+Sator Imaging
+
+:::
+
+## Original Readme
+:::note
+
+[](https://www.nuget.org/packages/SatorImaging.TDoubles)
+[](https://github.com/sator-imaging/TDoubles/actions/workflows/test.yml)
+
+[](https://deepwiki.com/sator-imaging/TDoubles)
+
+[๐บ๐ธ English](https://github.com/sator-imaging/TDoubles/README.md)
+ โ
+[๐ฏ๐ต ๆฅๆฌ่ช็](https://github.com/sator-imaging/TDoubles/README.ja.md)
+ โ
+[๐จ๐ณ ็ฎไฝไธญๆ็](https://github.com/sator-imaging/TDoubles/README.zh-CN.md)
+
+
+
+
+
+
+
+`TDoubles` is a powerful C# source generator that revolutionizes unit testing by creating mock wrapper classes at compile-time. Instead of relying on complex runtime reflection or proxy generation like traditional mocking frameworks, this generator produces clean, readable C# code during compilation that wraps your target types with customizable behavior.
+
+
+
+###### โจ Comparison with Traditional Mocking Frameworks
+
+| Feature | TDoubles | Traditional Frameworks (Moq, NSubstitute) |
+|---------|---------------------------|-------------------------------------------|
+| **Performance** | Zero runtime overhead, compile-time generation | Runtime reflection and proxy creation |
+| **Type Safety** | Full compile-time checking and IntelliSense | Runtime configuration, limited IntelliSense |
+| **Generic Support** | Full support including constraints | Limited generic type support |
+| **Setup Complexity** | Single attribute, minimal configuration | Complex fluent APIs and setup expressions |
+| **Debugging** | Generated code is readable and debuggable | Proxy objects can be difficult to debug |
+
+
+
+
+
+### โก Quick Start
+
+Apply the `[Mock]` attribute to a partial class, and the generator handles the rest.
+
+```cs
+using TDoubles;
+
+public interface IDataService
+{
+ string GetData(int id);
+ void SaveData(string data);
+}
+
+[Mock(typeof(IDataService))] // ๐
+partial class DataServiceMock
+{
+ // Implementation will be generated automatically
+}
+```
+
+
+Here shows how to use the mock in your code.
+
+```cs
+// Create the mock
+var mockService = new DataServiceMock();
+
+// Override behavior for testing
+mockService.MockOverrides.GetData = (id) => $"MockData_{id}";
+// ~~~~~~~~~~~~~
+
+string mockData = mockService.GetData(123); // Returns "MockData_123"
+```
+
+
+You can delegate to real implementation and override partial behaviour of the mock.
+
+```cs
+var mock = new DataServiceMock(new ConcreteDataService());
+
+// Use default behavior (delegates to real service)
+var realData = mock.GetData(123);
+
+// Override partial behaviour for testing
+mock.MockOverrides.SaveData = (data) => Console.WriteLine($"Saved: {data}");
+mock.SaveData(realData);
+```
+
+
+Implements fake behaviors for debugging purposes in conjunction with latest update to the real implementation.
+
+```cs
+[Mock(typeof(IFoo), nameof(IFoo.Save), nameof(IFoo.Load))]
+partial class FooFake
+{
+ public void Save() => File.WriteAllText("...", JsonUtility.ToJson(this, true));
+ public void Load() => JsonUtility.FromJsonOverwrite(File.ReadAllText("..."), this);
+}
+
+// Delegates to latest ConcreteFoo implementation except for Save and Load
+var fake = new FooFake(new ConcreteFoo());
+```
+
+
+
+
+###### Generic Type Support
+
+`TDoubles` provides support for generic type mocking on both unbound and closed constructed generics.
+
+```cs
+[Mock(typeof(IList))]
+partial class ListIntMock {}
+
+// Proper type constraint for TKey is automatically generated and
+// type parameter naming mismatch is also resolved
+[Mock(typeof(IDictioanry<,>))]
+partial class DictionaryMock {}
+```
+
+
+###### Efficient Callback Support
+
+There are efficient extension points to implement custom callback for each mock member call.
+
+> [!TIP]
+> As C# specification, `partial void` method call is completely removed from built assembly when method body is not implemented in your mock class declaration.
+>
+> https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/partial-member
+
+```cs
+[Mock(typeof(IList<>))]
+partial class ListSpy // ๐ต < Investigate suspects!
+{
+ readonly Dictionary _callCountByName = new();
+
+ // Without allocating object?[] instance
+ partial void OnWillMockCall(string memberName)
+ {
+ if (!_callCountByName.TryGetValue(memberName, out var current))
+ {
+ current = 0;
+ }
+ _callCountByName[memberName] = current + 1;
+ }
+
+ // Another overload can take arguments passed to mock member
+ // * Array.Empty() is used for parameterless members
+ partial void OnWillMockCall(string memberName, object?[] args)
+ {
+ // How to determine method overload
+ if (memberName == "Add")
+ {
+ if (args[0] is T)
+ {
+ Console.WriteLine("Add(T item) is invoked.");
+ }
+ else
+ {
+ Console.WriteLine("Add(object item) is invoked.");
+ }
+ }
+ }
+}
+```
+
+
+###### `Mock` Attribute Options
+
+There are options to select generated mock members.
+
+```cs
+// Include internal types, interfaces and members to mock generation
+[Mock(typeof(Foo), IncludeInternals = true)]
+partial class FooMock \{ }
+
+// Exclude specified members from mock generation (no error if member is not found)
+[Mock(typeof(Foo), "ToString", "Foo", "Bar", IncludeInternals = false)]
+partial class FooMockWithoutToStringOverride
+{
+ // You can re-implement excluded 'ToString' as you desired
+ public override string ToString() => base.ToString() ?? "";
+}
+```
+
+
+
+
+
+### Introduction
+
+The generator works by analyzing types marked with the `[Mock]` attribute and generating corresponding mock classes that delegate to the original implementation while providing override capabilities through a simple, strongly-typed API. This approach eliminates the performance overhead of reflection-based mocking while maintaining full type safety and IntelliSense support.
+
+
+###### Key Benefits
+
+- **Zero Runtime Overhead**: Mock classes are generated at compile-time, eliminating reflection costs and improving test execution performance
+- **Full Type Safety**: Generated mocks provide complete IntelliSense support, compile-time checking, and refactoring safety
+- **Minimal Setup**: Simply add the NuGet package, apply the `[Mock]` attribute to a partial class, and the generator handles the rest
+- **Universal Compatibility**: Supports all major C# type constructs including interfaces, classes, records, record structs, regular structs, and static classes
+- **Advanced Generic Support**: Handles complex generic scenarios including type constraints, nested generics, and generic method overloads
+- **Internal Member Access**: Optional `IncludeInternals` configuration allows mocking of internal members for comprehensive testing
+- **Clean Generated Code**: Produces human-readable, debuggable mock implementations that integrate seamlessly with your codebase
+
+
+###### Use Cases
+
+TDoubles generator excels in scenarios where you need:
+
+- **High-Performance Testing**: When test execution speed is critical and reflection overhead is unacceptable
+- **Legacy Code Testing**: Mocking existing classes and structs that weren't designed with interfaces
+- **Static Method Testing**: Converting static methods to testable instance methods through mock wrappers
+- **Record and Struct Mocking**: Testing value types and immutable records that traditional frameworks struggle with
+- **Complex Generic Testing**: Mocking generic types with multiple type parameters and constraints
+- **Internal API Testing**: Testing internal members without making them public
+
+
+###### How It Works
+
+1. **Mark Target Types**: Apply the `[Mock(typeof(TargetType))]` attribute to a partial class
+2. **Compile-Time Generation**: The source generator analyzes your target type and creates a mock implementation
+3. **Delegate with Overrides**: Generated mocks delegate to the original instance while providing `MockOverrides` for custom behavior
+4. **Test with Confidence**: Use the generated mock in your tests with full type safety and performance
+
+######### Delegation Logic
+
+Here is pseudo code of delegation. Actual code is more complicated as need to support `ref` and `out` parameter modifiers.
+
+```cs
+public string GetData(int id)
+{
+ // Returns 'default' if value type or nullable reference type otherwise throws
+ return MockOverrides.GetData?.Invoke(id)
+ ?? _target?.GetData(id)
+ ?? throw new TDoublesException(...);
+}
+```
+
+######### Generated Mock Structure
+
+When you create a mock class, the generator adds several members:
+
+```csharp
+[Mock(typeof(IUserService))]
+partial class UserServiceMock
+{
+ // Generated by source generator:
+
+ // Constructor that takes the target instance
+ public UserServiceMock(IUserService? target = default) \{ }
+
+ // Access to the underlying target
+ public IUserService? MockTarget \{ get; }
+
+ // Unified callback
+ partial void OnWillMockCall(string memberName);
+ partial void OnWillMockCall(string memberName, object?[] args);
+
+ // Override configuration object
+ public sealed partial class MockOverrideContainer \{ }
+ public MockOverrideContainer MockOverrides \{ get; }
+
+ // All interface/class members are implemented
+ public string GetUserName(int userId) \{ /* generated implementation */ }
+ public Task DeleteUser(int userId) \{ /* generated implementation */ }
+ // ... etc
+}
+```
+
+
+
+
+
+### Installation
+
+###### NuGet Package Installation
+
+######### Package Manager Console
+
+```powershell
+Install-Package SatorImaging.TDoubles
+```
+
+######### .NET CLI
+
+```bash
+dotnet add package SatorImaging.TDoubles
+```
+
+######### PackageReference (Manual)
+
+Add the following to your project file (`.csproj`):
+
+```xml
+
+```
+
+
+###### System Requirements
+
+- **.NET Framework**: .NET Standard 2.0 or higher
+- **C# Language Version**: C# 7.3 or later
+- **Compatible Runtimes**:
+ - .NET Framework 4.6.1+
+ - .NET Core 2.0+
+ - .NET 5.0+
+ - Unity 2022.3.12f1 or later
+
+
+###### Setup and Configuration
+
+######### Basic Setup
+
+1. Install the NuGet package using one of the methods above
+2. Rebuild your project to enable the source generator
+3. Create partial classes with the `[Mock]` attribute to generate mocks
+
+######### Project Configuration
+
+No additional project configuration is required. The source generator automatically activates when the package is installed and will generate mock classes during compilation.
+
+######### Verification
+
+To verify the installation was successful:
+
+1. Add a simple mock class to your project:
+ ```csharp
+ using TDoubles;
+
+ public interface ITestService
+ {
+ string GetMessage();
+ }
+
+ [Mock(typeof(ITestService))]
+ partial class TestServiceMock
+ {
+ // Mock implementation will be generated here
+ }
+ ```
+2. Build your project
+3. Check that no compilation errors occur and the mock class is generated
+
+
+######### IDE Support
+- **Visual Studio**: Full IntelliSense support for generated mock classes
+- **Visual Studio Code**: Works with C# extension
+- **JetBrains Rider**: Full support with code completion
+- **Command Line**: Works with `dotnet build` and `msbuild`
+
+
+
+
+
+### Basic Usage
+
+This section provides step-by-step examples to get you started with `TDoubles`. All examples are complete and ready to use in your projects.
+
+
+###### Prerequisites
+
+Before using the TDoubles generator, ensure your mock classes meet these requirements:
+
+1. **Partial Class**: Mock classes must be declared as `partial`
+2. **Mock Attribute**: Apply `[Mock(typeof(TargetType))]` to the partial class
+3. **Namespace**: Include `using TDoubles;`
+4. **Visibility**: Use any visibility modifier (public, internal, etc.) - generated members will inherit the same visibility
+
+
+###### Simple Interface Mocking
+
+The most common scenario is mocking interfaces for dependency injection testing.
+
+######### Example: User Service Interface
+
+```csharp
+using TDoubles;
+using System;
+using System.Threading.Tasks;
+
+// Define your interface
+public interface IUserService
+{
+ string GetUserName(int userId);
+ Task DeleteUser(int userId);
+ bool IsUserActive(int userId);
+}
+
+// Create a partial mock class
+[Mock(typeof(IUserService))]
+partial class UserServiceMock
+{
+ // The source generator will create the complete implementation here
+}
+
+// Example usage in tests
+class Program
+{
+ static void Main()
+ {
+ // Create a concrete implementation for delegation
+ var realService = new ConcreteUserService();
+
+ // Create the mock with the real service as the underlying target
+ var mockService = new UserServiceMock(realService);
+
+ Console.WriteLine("=== Default Behavior (Delegates to Real Service) ===");
+ Console.WriteLine($"User Name: {mockService.GetUserName(123)}");
+ Console.WriteLine($"Is Active: {mockService.IsUserActive(123)}");
+
+ Console.WriteLine("\n=== Custom Behavior with Overrides ===");
+
+ // Override specific methods for testing
+ mockService.MockOverrides.GetUserName = (userId) => $"MockUser_{userId}";
+ mockService.MockOverrides.IsUserActive = (userId) => userId > 100;
+
+ Console.WriteLine($"User Name (Overridden): {mockService.GetUserName(123)}");
+ Console.WriteLine($"Is Active (Overridden): {mockService.IsUserActive(50)}");
+ Console.WriteLine($"Is Active (Overridden): {mockService.IsUserActive(150)}");
+
+ // Access the underlying real service if needed
+ Console.WriteLine($"Real Service: {mockService.MockTarget.GetUserName(123)}");
+ }
+}
+
+// Concrete implementation for demonstration
+public class ConcreteUserService : IUserService
+{
+ public string GetUserName(int userId) => $"RealUser_{userId}";
+ public async Task DeleteUser(int userId) => await Task.FromResult(true);
+ public bool IsUserActive(int userId) => true;
+}
+```
+
+
+###### Class Mocking with Inheritance
+
+Mock concrete classes to test inheritance scenarios and virtual method overrides.
+
+######### Example: Service Class with Virtual Methods
+
+```csharp
+using TDoubles;
+using System;
+
+// Base service class with virtual methods
+public class DatabaseService
+{
+ public virtual string GetConnectionString() => "Server=localhost;Database=prod;";
+ public virtual void SaveData(string data) => Console.WriteLine($"Saving to database: {data}");
+ public virtual int GetRecordCount() => 1000;
+
+ // Non-virtual method (will be wrapped but not overridable)
+ public string GetServiceName() => "DatabaseService";
+}
+
+// Create mock for the class
+[Mock(typeof(DatabaseService))]
+partial class DatabaseServiceMock
+{
+ // Generated implementation will wrap all public methods
+}
+
+// Example usage
+class Program
+{
+ static void Main()
+ {
+ // Create real service instance
+ var realService = new DatabaseService();
+
+ // Create mock wrapper
+ var mockService = new DatabaseServiceMock(realService);
+
+ Console.WriteLine("=== Default Behavior ===");
+ Console.WriteLine($"Connection: {mockService.GetConnectionString()}");
+ Console.WriteLine($"Service Name: {mockService.GetServiceName()}");
+ Console.WriteLine($"Record Count: {mockService.GetRecordCount()}");
+ mockService.SaveData("test data");
+
+ Console.WriteLine("\n=== Testing Scenario Overrides ===");
+
+ // Override for testing scenarios
+ mockService.MockOverrides.GetConnectionString = () => "Server=testserver;Database=test;";
+ mockService.MockOverrides.GetRecordCount = () => 0; // Simulate empty database
+ mockService.MockOverrides.SaveData = (data) => Console.WriteLine($"TEST MODE: Would save '{data}'");
+
+ Console.WriteLine($"Test Connection: {mockService.GetConnectionString()}");
+ Console.WriteLine($"Test Record Count: {mockService.GetRecordCount()}");
+ mockService.SaveData("test data");
+
+ // Non-virtual methods still work but delegate to original
+ Console.WriteLine($"Service Name (always delegates): {mockService.GetServiceName()}");
+ }
+}
+```
+
+
+###### Inheritance and Interface Implementation
+
+Mock classes that both inherit from base classes and implement interfaces.
+
+######### Example: Complex Service Hierarchy
+
+```csharp
+using TDoubles;
+using System;
+
+// Interface definition
+public interface INotificationService
+{
+ void SendNotification(string message);
+ bool IsServiceAvailable();
+}
+
+// Base class with virtual methods
+public class BaseService
+{
+ public virtual string GetServiceType() => "Base";
+ public virtual void Initialize() => Console.WriteLine("Base initialization");
+}
+
+// Concrete class that inherits and implements interface
+public class EmailService : BaseService, INotificationService
+{
+ public override string GetServiceType() => "Email";
+ public override void Initialize() => Console.WriteLine("Email service initialized");
+
+ public void SendNotification(string message) => Console.WriteLine($"Email: {message}");
+ public bool IsServiceAvailable() => true;
+}
+
+// Mock the concrete class
+[Mock(typeof(EmailService))]
+partial class EmailServiceMock
+{
+ // Mocks both inherited methods and interface implementations
+}
+
+// Usage example
+class Program
+{
+ static void Main()
+ {
+ var realService = new EmailService();
+ var mockService = new EmailServiceMock(realService);
+
+ Console.WriteLine("=== Testing Inherited Methods ===");
+ Console.WriteLine($"Service Type: {mockService.GetServiceType()}");
+ mockService.Initialize();
+
+ Console.WriteLine("\n=== Testing Interface Methods ===");
+ mockService.SendNotification("Hello World");
+ Console.WriteLine($"Available: {mockService.IsServiceAvailable()}");
+
+ Console.WriteLine("\n=== Testing with Overrides ===");
+
+ // Override inherited method
+ mockService.MockOverrides.GetServiceType = () => "MockEmail";
+ mockService.MockOverrides.Initialize = () => Console.WriteLine("Mock initialization");
+
+ // Override interface methods
+ mockService.MockOverrides.SendNotification = (msg) => Console.WriteLine($"MOCK EMAIL: {msg}");
+ mockService.MockOverrides.IsServiceAvailable = () => false;
+
+ Console.WriteLine($"Service Type: {mockService.GetServiceType()}");
+ mockService.Initialize();
+ mockService.SendNotification("Test Message");
+ Console.WriteLine($"Available: {mockService.IsServiceAvailable()}");
+ }
+}
+```
+
+
+
+
+
+### Advanced Usage
+
+For advanced scenarios including generic types, static classes, records, structs, and internal member access, see the [Advanced Usage Guide](https://github.com/sator-imaging/TDoubles/docs/advanced-usage.md).
+
+
+
+
+
+### Testing Examples
+
+For comprehensive testing examples with MSTest, NUnit, and performance comparisons, see the [Testing Examples Guide](https://github.com/sator-imaging/TDoubles/docs/testing-examples.md).
+
+
+
+
+
+
+
+
+
+
+### Technical Note
+
+###### `record` and `record struct`
+
+- Always implements `IEquatable` and `MockOverrides.MockTargetRecord_Equals`
+ - Note that it is *NOT* `IEquatable`
+- `bool Equals(object?)` cannot be overridden
+
+
+
+
+
+### Known Limitations and Unsupported Scenarios
+
+###### Type Parameters of Generic Method
+
+When method uses method-level type parameter instead of type-level parameter, `MockOverrides` will use `object` instead of method-level type parameter.
+
+```cs
+// Generated mock has type-level parameter T
+partial class Mock
+{
+ // Generated mock method that has T and TMethod type parameter
+ public TMethod GenericMethod(T input) \{ ... }
+
+ // can be added to this class but it must also be exposed as type-level parameter...
+ public sealed partial class MockOverrideContainer
+ {
+ // type-level parameter T is used but TMethod is shadowed to object
+ public Func GenericMethod \{ get; set; }
+ // ~~~~~~ Not TMethod
+ }
+}
+```
+
+> [!NOTE]
+> Generated mock method returns `TMethod` as mock target does. Internally, mock method will cast `object` result from override to `TMethod` when returning value.
+
+
+###### Type System Limitations
+
+**Unsupported Types:**
+- Enums (use wrapper classes instead)
+- Delegates and function pointers
+- Primitive types (`int`, `string`, etc.)
+- Static classes with only static constructors
+- Abstract classes with pure virtual methods requiring implementation
+- `object`, `ValueType`, `Enum` and other special types such as `Span`
+
+
+###### Type Constraint Limitations
+
+**Unsupported Constraints:**
+- `where T : default`
+- `where T : allows ref struct`
+
+
+###### Return Type Limitations
+
+**Unsupported Type:**
+- `ref` return type
+
+
+###### Attribute Limitations
+
+Attributes on type, method, property or etc are not preserved in generated mock.
+
+
+###### Method and Property Limitations
+
+**Unsupported Members:**
+- `ref` and `out` parameters in some complex scenarios (?)
+- ~~Methods with `__arglist` (variable arguments)~~
+- ~~Explicit interface implementations with name conflicts~~
+- ~~Properties with complex getter/setter accessibility combinations~~
+
+**Partial Support:**
+
+```csharp
+public interface IService
+{
+ // โ
Fully supported
+ string GetData(int id);
+ Task ProcessAsync(string data);
+
+ // โ ๏ธ Limited support - may not override correctly
+ ref int GetReference();
+ void ProcessData(__arglist);
+}
+```
+
+
+###### Generic Method Limitations
+
+Some valid type constraint is not transformed correctly. We have no plan to support this edge case of type constraint.
+
+> Note: `override` method cannot have type constraint except for `class` and `struct`.
+
+```cs
+// Abstract method declaration that returns (M, N?) with where M : N? constraint
+public abstract (M t, N? u) TypeArgMappingNullable_Abstract() where M : N?;
+
+// Expected (valid) return type is (M, N)
+public override (M t, N u) TypeArgMappingNullable_Abstract() \{ }
+
+// But got (M, N?)
+public override (M t, N? u) TypeArgMappingNullable_Abstract() \{ }
+```
+
+
+
+###### Inheritance and Interface Limitations
+
+**Multiple Interface Implementation:**
+- ~~Supported, but explicit interface implementations may have naming conflicts~~
+- Diamond inheritance patterns may cause method resolution issues
+
+**Virtual Method Overriding:**
+- ~~Only `virtual` and `abstract` methods can be overridden in class mocks~~
+- `sealed` methods cannot be overridden (will delegate to original)
+
+
+###### Platform and Framework Limitations
+
+**Framework Support:**
+- Requires .NET Standard 2.0 or higher
+- Source generators require C# 7.3 or later
+- Some advanced C# 11+ features may not be fully supported
+
+**IDE Integration:**
+- IntelliSense may be delayed for newly generated mocks
+- Some IDEs may require rebuild to recognize generated code
+- Debugging generated code may show optimized/synthetic code
+
+
+
+
+
+### Contributing
+
+We welcome and appreciate contributions from the community! Whether you're fixing bugs, adding features, improving documentation, or providing feedback, your contributions help make TDoubles better for everyone.
+
+See [CONTRIBUTING.md](https://github.com/sator-imaging/TDoubles/CONTRIBUTING.md)
+
+
+
+
+
+### Code of Conduct
+
+We are committed to providing a welcoming and inclusive environment for all contributors. Please be respectful and professional in all interactions.
+
+
+
+
+
+### Support and Community
+
+###### Getting Help
+
+If you encounter issues not covered in this troubleshooting guide:
+
+1. **Check the GitHub Issues:** Search existing issues for similar problems
+2. **Create a Minimal Reproduction:** Provide the smallest possible code example that demonstrates the issue
+3. **Include Build Output:** Share relevant compiler errors and warnings
+4. **Specify Environment:** Include .NET version, IDE, and operating system details
+
+**Support Channels:**
+- [GitHub Discussions](https://github.com/sator-imaging/TDoubles/discussions) - Questions and community support
+- [GitHub Issues](https://github.com/sator-imaging/TDoubles/issues) - Bug reports and feature requests
+
+
+###### Reporting Security Issues
+
+If you discover a security vulnerability, please report it privately by emailing the maintainers rather than creating a public issue. This allows us to address the issue before it becomes widely known.
+
+
+
+
+
+### Project Information
+
+###### TODO: Help Wanted
+
+- Missing Tests
+ - `static` class mocking
+ - `sealed` overridden methods
+ - `async` tests
+ - `event` getter and setter tests
+ - `readonly struct` tests
+ - `readonly record struct` tests
+ - `Tuple` and `ValueTuple` tests
+ - Property and indexer accessibility tests (e.g., `{ get; private set; }` or etc)
+- Missing Features
+ - `ref` return
+ - Attribute preservation
+ - Add proper ` ` for mock members
+ - Support for `default` and `allows ref struct` type constraint
+ - The `default` constraint is valid on override and explicit interface implementation methods only
+ - Roslyn update is required while keeping Unity engine support
+ - Emit diagnostic error on type parameters
+- Optimization
+ - Use `ImmutableArray` or `ImmutableList` as possible
+ - Eliminate inefficient `StringBuilder` use
+- Refactor
+ - Eliminate FP programming techniques
+ - Transform data model to domain model to encapsulate information and behaviour
+ - Centralize blueprint-to-C# conversion in domain model to make it consistent, robust and maintainable
+ - Eliminate duplicate functions, control flows and etc scattered in codebase
+- Optional
+ - ~~New `Mock` attribute option to generate `MockCallCounts` that records the call count of each mock member~~
+ - Declare `volatile int` fields
+ - Increment count by `Interlocked.Increment(ref ...)` method at the beginning of generated mock class member.
+
+
+###### Author and Maintainer
+
+**Sator Imaging**
+- GitHub: [@sator-imaging](https://github.com/sator-imaging)
+- Project Repository: [sator-imaging/TDoubles](https://github.com/sator-imaging/TDoubles)
+
+
+###### Acknowledgments
+
+We thank all contributors who have helped improve this project through code contributions, bug reports, feature suggestions, and community support.
+
+
+###### License
+
+This project is licensed under the **MIT License**.
+
+######### Third-Party Licenses
+
+This project uses the following third-party packages:
+- **Microsoft.CodeAnalysis.CSharp** (MIT License)
+- **Microsoft.CodeAnalysis.Analyzers** (MIT License)
+
+
+
+
+
+---
+
+**© 2025 Sator Imaging. All rights reserved.**
+
+For support, questions, or contributions, please visit our [GitHub repository](https://github.com/sator-imaging/TDoubles).
+
+
+:::
+
+### About
+:::note
+
+Generating test stubs with mocking for interfaces
+
+
+:::
+
+## How to use
+
+### Example (source csproj, source files)
+
+
+
+
+
+This is the CSharp Project that references **SatorImaging.TDoubles**
+```xml showLineNumbers {17}
+
+
+
+ net10.0
+ enable
+ enable
+
+ false
+ true
+
+
+
+
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
+
+
+
+ true
+ $(BaseIntermediateOutputPath)\GX
+
+
+
+
+```
+
+
+
+
+
+ This is the use of **SatorImaging.TDoubles** in *IMyClock.cs*
+
+```csharp showLineNumbers
+namespace MockData;
+
+public interface IMyClock
+{
+ public DateTime GetNow();
+ public DateTime GetUtcNow();
+}
+```
+
+
+
+
+ This is the use of **SatorImaging.TDoubles** in *TestClock.cs*
+
+```csharp showLineNumbers
+
+
+namespace TestClock;
+
+[TDoubles.Mock(typeof(IMyClock))]
+public partial class QuickStartRepoStub \{ }
+
+
+[TestClass]
+public class TestClock
+{
+ [TestMethod]
+ public void TestMyClock()
+ {
+ var expectations = new QuickStartRepoStub();
+ expectations.MockOverrides.GetNow =()=>(DateTime.Now.AddYears(-1));
+
+ IMyClock mock = expectations;
+ var data= mock.GetNow();
+ Assert.AreEqual(DateTime.Now.Year -1, data.Year);
+
+ }
+}
+
+
+
+```
+
+
+
+
+### Generated Files
+
+Those are taken from $(BaseIntermediateOutputPath)\GX
+
+
+
+
+```csharp showLineNumbers
+#nullable enable
+
+namespace TestClock
+{
+ partial class QuickStartRepoStub
+ : global::MockData.IMyClock
+ {
+ public QuickStartRepoStub(global::MockData.IMyClock? target = default)
+ {
+ _target = target;
+ MockOverrides = new MockOverrideContainer();
+ }
+
+ ///
+ /// Gets the underlying target instance being mocked.
+ ///
+ public global::MockData.IMyClock? MockTarget => _target;
+
+ private readonly global::MockData.IMyClock? _target;
+
+ ///
+ /// Gets the container for method and property overrides.
+ ///
+ public MockOverrideContainer MockOverrides \{ get; }
+
+ public global::System.DateTime GetNow()
+ {
+ OnWillMockCall("GetNow"); OnWillMockCall("GetNow", global::System.Array.Empty());
+
+ global::System.DateTime __STMG_2025_08_25__ = default!;
+ if (MockOverrides.GetNow != null) {
+ __STMG_2025_08_25__ = (global::System.DateTime)MockOverrides.GetNow.Invoke();
+ }
+
+ return (global::System.DateTime)((MockOverrides.GetNow != null ? __STMG_2025_08_25__ : (_target != null ? ((global::MockData.IMyClock?)_target)!.GetNow() : default)));
+ }
+
+ public global::System.DateTime GetUtcNow()
+ {
+ OnWillMockCall("GetUtcNow"); OnWillMockCall("GetUtcNow", global::System.Array.Empty());
+
+ global::System.DateTime __STMG_2025_08_25__ = default!;
+ if (MockOverrides.GetUtcNow != null) {
+ __STMG_2025_08_25__ = (global::System.DateTime)MockOverrides.GetUtcNow.Invoke();
+ }
+
+ return (global::System.DateTime)((MockOverrides.GetUtcNow != null ? __STMG_2025_08_25__ : (_target != null ? ((global::MockData.IMyClock?)_target)!.GetUtcNow() : default)));
+ }
+
+ public override bool Equals(object? obj)
+ {
+ OnWillMockCall("Equals"); OnWillMockCall("Equals", new object?[] \{ obj });
+
+ bool __STMG_2025_08_25__ = default!;
+ if (MockOverrides.Equals != null) {
+ __STMG_2025_08_25__ = (bool)MockOverrides.Equals.Invoke(obj);
+ }
+
+ return (bool)((MockOverrides.Equals != null ? __STMG_2025_08_25__ : (_target != null ? ((object?)_target)!.Equals(obj) : default)));
+ }
+
+ public override int GetHashCode()
+ {
+ OnWillMockCall("GetHashCode"); OnWillMockCall("GetHashCode", global::System.Array.Empty());
+
+ int __STMG_2025_08_25__ = default!;
+ if (MockOverrides.GetHashCode != null) {
+ __STMG_2025_08_25__ = (int)MockOverrides.GetHashCode.Invoke();
+ }
+
+ return (int)((MockOverrides.GetHashCode != null ? __STMG_2025_08_25__ : (_target != null ? ((object?)_target)!.GetHashCode() : default)));
+ }
+
+ public override string? ToString()
+ {
+ OnWillMockCall("ToString"); OnWillMockCall("ToString", global::System.Array.Empty());
+
+ string? __STMG_2025_08_25__ = default!;
+ if (MockOverrides.ToString != null) {
+ __STMG_2025_08_25__ = (string?)MockOverrides.ToString.Invoke();
+ }
+
+ return (string?)((MockOverrides.ToString != null ? __STMG_2025_08_25__ : (_target != null ? ((object?)_target)!.ToString() : default)));
+ }
+
+ ///
+ /// Container for method and property overrides.
+ ///
+ public sealed partial class MockOverrideContainer
+ {
+ ///
+ /// Override for global::MockData.IMyClock.GetNow method:+ DateTime GetNow()
+ ///
+ public global::System.Func? GetNow \{ get; set; }
+
+ ///
+ /// Override for global::MockData.IMyClock.GetUtcNow method:+ DateTime GetUtcNow()
+ ///
+ public global::System.Func? GetUtcNow \{ get; set; }
+
+ ///
+ /// Override for object.Equals method:+ bool Equals(object? obj)
+ ///
+ public new global::System.Func? Equals \{ get; set; }
+
+ ///
+ /// Override for object.GetHashCode method:+ int GetHashCode()
+ ///
+ public new global::System.Func? GetHashCode \{ get; set; }
+
+ ///
+ /// Override for object.ToString method:+ string? ToString()
+ ///
+ public new global::System.Func? ToString \{ get; set; }
+
+ }
+
+ ///
+ /// The unified callback invoked before each mock call.
+ ///
+ /// The short name of original member without generic type parameters.
+ partial void OnWillMockCall(string memberName);
+
+ ///
+ /// The unified callback invoked before each mock call.
+ ///
+ /// The short name of original member without generic type parameters.
+ partial void OnWillMockCall(string memberName, object?[] args);
+ }
+}
+
+```
+
+
+
+
+```csharp showLineNumbers
+using System;
+
+namespace TDoubles
+{
+ ///
+ /// Attribute used to mark partial classes for mock generation.
+ /// The source generator will create a mock implementation that delegates to the target type.
+ ///
+ [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = false)]
+ internal sealed class MockAttribute : Attribute
+ {
+ ///
+ /// Gets the target type to be mocked.
+ ///
+ public Type TargetType \{ get; }
+
+ ///
+ /// Gets or sets whether to include internal members in the mock.
+ /// Default is false (public members only).
+ ///
+ public bool IncludeInternals \{ get; set; \} = false;
+
+ ///
+ /// Gets or sets the short names of members to exclude from the generated mock.
+ ///
+ public string[] ExcludeMemberShortNames \{ get; set; \} = Array.Empty();
+
+ ///
+ /// Initializes a new instance of the MockAttribute with the specified target type.
+ ///
+ /// The type to be mocked. Cannot be null.
+ /// Optional: Short names of members to exclude from the generated mock.
+ /// Thrown when targetType is null.
+ public MockAttribute(Type targetType, params string[] excludeMemberShortNames)
+ {
+ TargetType = targetType ?? throw new ArgumentNullException(nameof(targetType));
+ ExcludeMemberShortNames = excludeMemberShortNames ?? Array.Empty();
+ }
+ }
+}
+```
+
+
+
+
+```csharp showLineNumbers
+using System;
+
+namespace TDoubles
+{
+ ///
+ /// Exception thrown when a mock member has no override and returns a non-nullable reference type.
+ /// This exception provides clear information about which member lacks a mock implementation.
+ ///
+ internal class TDoublesException : Exception
+ {
+ ///
+ /// Gets the name of the member that lacks a mock implementation.
+ ///
+ public string MemberName \{ get; }
+
+ ///
+ /// Gets the name of the type that contains the member.
+ ///
+ public string TypeName \{ get; }
+
+ ///
+ /// Initializes a new instance of the TDoublesException with the specified member and type names.
+ ///
+ /// The name of the member that lacks a mock implementation. Cannot be null.
+ /// The name of the type that contains the member. Cannot be null.
+ public TDoublesException(string memberName, string typeName)
+ : base($"No mock override provided for member '{memberName}' in type '{typeName}' that returns a non-nullable reference type.")
+ {
+ MemberName = memberName ?? throw new ArgumentNullException(nameof(memberName));
+ TypeName = typeName ?? throw new ArgumentNullException(nameof(typeName));
+ }
+
+ ///
+ /// Initializes a new instance of the TDoublesException with the specified member name, type name, and custom message.
+ ///
+ /// The name of the member that lacks a mock implementation. Cannot be null.
+ /// The name of the type that contains the member. Cannot be null.
+ /// The custom error message.
+ public TDoublesException(string memberName, string typeName, string message)
+ : base(message)
+ {
+ MemberName = memberName ?? throw new ArgumentNullException(nameof(memberName));
+ TypeName = typeName ?? throw new ArgumentNullException(nameof(typeName));
+ }
+ }
+}
+```
+
+
+
+
+## Useful
+
+### Download Example (.NET C#)
+
+:::tip
+
+[Download Example project SatorImaging.TDoubles ](/sources/SatorImaging.TDoubles.zip)
+
+:::
+
+
+### Share SatorImaging.TDoubles
+
+
+
+https://ignatandrei.github.io/RSCG_Examples/v2/docs/SatorImaging.TDoubles
+
+
+
diff --git a/v2/rscg_examples_site/docs/RSCG-Examples/index.md b/v2/rscg_examples_site/docs/RSCG-Examples/index.md
index 84a4a91ce..67311e430 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: 285 RSCG list by category
-description: 285 RSCG list by category
+title: 286 RSCG list by category
+description: 286 RSCG list by category
slug: /rscg-examples
---
@@ -1688,7 +1688,7 @@ import DocCardList from '@theme/DocCardList';
## Tests
- Expand Tests =>examples:9
+ Expand Tests =>examples:10
@@ -1734,6 +1734,11 @@ import DocCardList from '@theme/DocCardList';
[TinyBDD.MSTest](/docs/TinyBDD.MSTest)
+
+
+
+[SatorImaging.TDoubles](/docs/SatorImaging.TDoubles)
+
@@ -2338,6 +2343,8 @@ flowchart LR;
Tests--> TinyBDD.MSTest((TinyBDD.MSTest))
+ Tests--> SatorImaging.TDoubles((SatorImaging.TDoubles))
+
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 1a11b36bc..3c5a30782 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 285 Roslyn Source Code Generator (RSCG)
+of 286 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 1ad98a1b0..9728d6125 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';
-## 285 RSCG with examples in descending chronological order
+## 286 RSCG with examples in descending chronological order
-This is the list of 285 ( 16 from Microsoft) RSCG with examples
+This is the list of 286 ( 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 285 ( 16 from Microsoft) RSCG with examples
| No | Name | Date | Category |
| --------- | ----- | ---- | -------- |
+|286| [SatorImaging.TDoubles by Sator Imaging ](/docs/SatorImaging.TDoubles)|2026-08-27 => 27 August 2026 | [Tests](/docs/Categories/Tests) |
|285| [PropertyResolvers by Tom Biddulph ](/docs/PropertyResolvers)|2026-08-26 => 26 August 2026 | [EnhancementProject](/docs/Categories/EnhancementProject) |
|284| [Pinecone.TypedPath by Nick Pinecone ](/docs/Pinecone.TypedPath)|2026-08-24 => 24 August 2026 | [FilesToCode](/docs/Categories/FilesToCode) |
|283| [IncrementalSourceGenerator.BuilderPattern by Martin Barrett Nielsen ](/docs/IncrementalSourceGenerator.BuilderPattern)|2026-08-23 => 23 August 2026 | [Builder](/docs/Categories/Builder) |
diff --git a/v2/rscg_examples_site/src/components/HomepageFeatures/index.js b/v2/rscg_examples_site/src/components/HomepageFeatures/index.js
index e0f5467ce..e97576692 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: '285 Examples (16 from MSFT)',
+title: '286 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 47748093e..7ba28fd85 100644
--- a/v2/rscg_examples_site/static/exports/RSCG.json
+++ b/v2/rscg_examples_site/static/exports/RSCG.json
@@ -2281,6 +2281,14 @@
"Source": "https://github.com/tombiddulph/PropertyResolvers",
"Category": "EnhancementProject",
"AddedOn": "2026-08-26T00:00:00"
+ },
+ {
+ "Name": "SatorImaging.TDoubles",
+ "Link": "https://ignatandrei.github.io/RSCG_Examples/v2/docs/SatorImaging.TDoubles",
+ "NuGet": "https://www.nuget.org/packages/SatorImaging.TDoubles/",
+ "Source": "https://github.com/sator-imaging/TDoubles",
+ "Category": "Tests",
+ "AddedOn": "2026-08-27T00: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 4db423cb9..0bdeccf4b 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/SatorImaging.TDoubles.zip b/v2/rscg_examples_site/static/sources/SatorImaging.TDoubles.zip
new file mode 100644
index 000000000..c270d6499
Binary files /dev/null and b/v2/rscg_examples_site/static/sources/SatorImaging.TDoubles.zip differ