Warning
The following document was generated by AI and HAS NOT YET been reviewed.
API Reference
This section provides comprehensive documentation for the MockAttribute class, configuration options, supported target types, and naming conventions used by TDoubles.
MockAttribute Class
The MockAttribute
is the core attribute used to mark partial classes for mock generation. It specifies the target type to be mocked and provides configuration options for customizing the generated mock behavior.
Class Declaration
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
internal class MockAttribute : Attribute
Constructor
public MockAttribute(Type targetType)
Parameters:
targetType
(Type): The target type that this mock will wrap. Cannot be null.
Exceptions:
ArgumentNullException
: Thrown when targetType is null.
Properties
TargetType
public Type TargetType { get; }
Gets the target type that this mock will wrap. This property is set through the constructor and cannot be modified after instantiation.
Example:
[Mock(typeof(IUserService))]
partial class UserServiceMock
{
// TargetType will be typeof(IUserService)
}
IncludeInternals
public bool IncludeInternals { get; set; } = false;
Gets or sets whether internal members should be included in the generated mock. When true
, both public and internal members will be mocked. When false
(default), only public members will be mocked.
Default Value: false
Example:
// Include only public members (default)
[Mock(typeof(InternalService))]
partial class InternalServiceMock { }
// Include both public and internal members
[Mock(typeof(InternalService), IncludeInternals = true)]
partial class InternalServiceMockWithInternals { }
Configuration Options
Basic Configuration
The most basic configuration requires only specifying the target type:
[Mock(typeof(IMyService))]
partial class MyServiceMock
{
// Minimal configuration - only public members will be mocked
}
Visibility Configuration
Mock classes can use any visibility modifier. The generated code automatically inherits the same visibility:
// Public mock - generated members will be public
[Mock(typeof(IMyService))]
public partial class PublicMyServiceMock { }
// Internal mock - generated members will be internal
[Mock(typeof(IMyService))]
internal partial class InternalMyServiceMock { }
// Default visibility - generated members will be internal (default for classes)
[Mock(typeof(IMyService))]
partial class DefaultMyServiceMock { }
Internal Member Access
To include internal members in the generated mock, set the IncludeInternals
property to true
:
[Mock(typeof(InternalService), IncludeInternals = true)]
partial class InternalServiceMock
{
// Both public and internal members will be mocked
}
Use Cases for IncludeInternals:
- Testing internal APIs without making them public
- Mocking classes with internal virtual methods
- Comprehensive testing of library internals
- Legacy code testing where internal members need mocking
Requirements for IncludeInternals:
- The target assembly must grant
InternalsVisibleTo
access to the test assembly - Internal members must be accessible from the mock class location
- Only applies to virtual or abstract internal members for class mocking
Generic Type Configuration
For generic target types, the mock class must have matching generic type parameters:
// Single generic parameter
[Mock(typeof(GenericService<>))]
partial class GenericServiceMock<T>
{
// T must match the target type's generic parameter
}
// Multiple generic parameters with constraints
[Mock(typeof(MultiGenericService<,>))]
partial class MultiGenericServiceMock<T, U> where T : class where U : struct
{
// Type constraints must match the target type
}
Supported Target Types
TDoubles generator supports the following C# type constructs:
Classes
Instance Classes:
- Regular classes with virtual/abstract members
- Classes with inheritance hierarchies
- Classes implementing interfaces
- Generic classes with type parameters and constraints
// Regular class
[Mock(typeof(DatabaseService))]
partial class DatabaseServiceMock { }
// Generic class
[Mock(typeof(GenericService<>))]
partial class GenericServiceMock<T> { }
// Class with constraints
[Mock(typeof(ConstrainedService<>))]
partial class ConstrainedServiceMock<T> where T : class, new() { }
Static Classes:
- Static classes with static methods and properties
- Utility classes and helper classes
- Extension method containers (methods become instance methods)
[Mock(typeof(StaticUtility))]
partial class StaticUtilityMock { }
Interfaces
All interface types are supported, including:
- Simple interfaces
- Generic interfaces
- Interfaces with inheritance hierarchies
- Interfaces with complex method signatures
// Simple interface
[Mock(typeof(IUserService))]
partial class UserServiceMock { }
// Generic interface
[Mock(typeof(IRepository<>))]
partial class RepositoryMock<T> { }
// Interface hierarchy
[Mock(typeof(IAdvancedService))] // inherits from IBaseService
partial class AdvancedServiceMock { }
Records
Both record classes and record structs are fully supported:
// Record class
[Mock(typeof(PersonRecord))]
partial class PersonRecordMock { }
// Record struct
[Mock(typeof(PointRecord))]
partial class PointRecordMock { }
// Generic record
[Mock(typeof(GenericRecord<>))]
partial class GenericRecordMock<T> { }
Structs
Regular structs and readonly structs are supported:
// Regular struct
[Mock(typeof(Point))]
partial class PointMock { }
// Readonly struct
[Mock(typeof(ReadOnlyPoint))]
partial class ReadOnlyPointMock { }
// Generic struct
[Mock(typeof(GenericStruct<>))]
partial class GenericStructMock<T> { }
Unsupported Types
The following types are not supported and will generate MOCK005
diagnostic errors:
- Enums
- Delegates
- Primitive types (int, string, bool, etc.)
- Array types
- Pointer types
- Dynamic types
Naming Conventions
TDoubles generator follows consistent naming conventions for generated types, files, and members.
Type Names
Non-Generic Types: Generated mock classes use the exact name specified in the partial class declaration.
[Mock(typeof(UserService))]
partial class UserServiceMock // Generated class name: UserServiceMock
Generic Types: Generic mock classes preserve the generic type parameters from the partial class declaration.
[Mock(typeof(GenericService<>))]
partial class GenericServiceMock<T> // Generated class name: GenericServiceMock<T>
Hint Name Generation
Hint names are used for generated source files and follow these patterns:
Non-Generic Types:
{ClassName}.g.cs
Generic Types:
{ClassName}T{TypeParameterCount}.g.cs
Examples:
UserServiceMock.g.cs
- Non-generic mockGenericServiceMockT1.g.cs
- Single generic parameterMultiServiceMockT2.g.cs
- Two generic parameters
With Namespaces:
{Namespace}.{ClassName}[T{Count}].g.cs
Examples:
MyApp.Services.UserServiceMock.g.cs
MyApp.Services.GenericServiceMockT1.g.cs
Member Names
Method Names: Generated mock methods preserve the original method names without modification.
// Original method
public string GetUserName(int id);
// Generated mock method (same name)
public string GetUserName(int id);
Property Names: Generated mock properties preserve the original property names.
// Original property
public string ConnectionString { get; set; }
// Generated mock property (same name)
public string ConnectionString { get; set; }
Override Property Names:
Override properties in the MockOverrides
object use the original member names, with suffixes for overloaded methods.
// Single method
mockService.MockOverrides.GetUserName = (id) => $"Mock_{id}";
// Overloaded methods get suffixes based on parameter types
mockService.MockOverrides.Process_T = (item) => { /* single parameter */ };
mockService.MockOverrides.Process_T_U = (item, value) => { /* two parameters */ };
Generated Member Structure
Every generated mock class includes these standard members:
Constructor:
public {MockClassName}({TargetType} target)
Target Access:
public {TargetType}? MockTarget { get; }
Override Configuration:
public MockOverrideContainer MockOverrides { get; }
Override Class:
public class MockOverrideContainer
{
// Override properties for each mockable member
public Func<{Parameters}, {ReturnType}>? {MethodName} { get; set; }
public Func<{ReturnType}>? {PropertyName}_Get { get; set; }
public Action<{ValueType}>? {PropertyName}_Set { get; set; }
}
Best Practices
Choosing Configuration Options
Use IncludeInternals when:
- Testing internal APIs that cannot be made public
- Working with legacy code that has internal virtual methods
- Comprehensive testing requires access to internal members
- The test assembly has
InternalsVisibleTo
access
Avoid IncludeInternals when:
- Only public API testing is required (default behavior is sufficient)
- Performance is critical (fewer members = faster compilation)
- The internal members are not virtual/abstract (won't be mockable anyway)
Generic Type Patterns
Prefer explicit type parameters:
// ✅ Good - explicit and clear
[Mock(typeof(Repository<>))]
partial class RepositoryMock<T> where T : class { }
Match constraints exactly:
// Target type
public class Service<T, U> where T : class where U : struct, IComparable<U>
// ✅ Correct mock with matching constraints
[Mock(typeof(Service<,>))]
partial class ServiceMock<T, U> where T : class where U : struct, IComparable<U> { }
Naming Conventions
Use descriptive mock class names:
// ✅ Good - clearly indicates it's a mock
[Mock(typeof(UserService))]
partial class UserServiceMock { }
// ❌ Avoid - unclear purpose
[Mock(typeof(UserService))]
partial class TestUserService { }
Follow project naming patterns:
// If your project uses "Mock" suffix
[Mock(typeof(IRepository))]
partial class RepositoryMock { }
// If your project uses "Test" prefix
[Mock(typeof(IRepository))]
partial class TestRepository { }