Table of Contents
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 mock
  • GenericServiceMockT1.g.cs - Single generic parameter
  • MultiServiceMockT2.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 { }