RSCG – SatorImaging.TDoubles

RSCG – SatorImaging.TDoubles
 
 

name SatorImaging.TDoubles
nuget https://www.nuget.org/packages/SatorImaging.TDoubles/
link https://github.com/sator-imaging/TDoubles
author Sator Imaging

Generating test stubs with mocking for interfaces

 

This is how you can use SatorImaging.TDoubles .

The code that you start with is


<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>net10.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>

    <IsPackable>false</IsPackable>
    <IsTestProject>true</IsTestProject>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.5.0" />
    <PackageReference Include="MSTest.TestAdapter" Version="2.2.10" />
    <PackageReference Include="MSTest.TestFramework" Version="2.2.10" />
    <PackageReference Include="coverlet.collector" Version="3.2.0" />
    <PackageReference Include="SatorImaging.TDoubles" Version="1.0.0">
      <PrivateAssets>all</PrivateAssets>
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
    </PackageReference>
  </ItemGroup>

  <ItemGroup>
    <ProjectReference Include="..\Mock\MockData.csproj" />
  </ItemGroup>
	<PropertyGroup>
		<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
		<CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\GX</CompilerGeneratedFilesOutputPath>
	</PropertyGroup>

</Project>


The code that you will use is


namespace MockData;

public interface IMyClock
{
    public DateTime GetNow();
    public DateTime GetUtcNow();
}




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);
        
    }
}




 

The code that is generated is

#nullable enable

namespace TestClock
{
    partial class QuickStartRepoStub
        : global::MockData.IMyClock
    {
        public QuickStartRepoStub(global::MockData.IMyClock? target = default)
        {
            _target = target;
            MockOverrides = new MockOverrideContainer();
        }

        /// <summary>
        /// Gets the underlying target instance being mocked.
        /// </summary>
        public global::MockData.IMyClock? MockTarget => _target;

        private readonly global::MockData.IMyClock? _target;

        /// <summary>
        /// Gets the container for method and property overrides.
        /// </summary>
        public MockOverrideContainer MockOverrides { get; }

        public global::System.DateTime GetNow()
        {
            OnWillMockCall("GetNow"); OnWillMockCall("GetNow", global::System.Array.Empty<object>());

            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<object>());

            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<object>());

            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<object>());

            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)));
        }

        /// <summary>
        /// Container for method and property overrides.
        /// </summary>
        public sealed partial class MockOverrideContainer
        {
            /// <summary>
            /// Override for global::MockData.IMyClock.GetNow method:<br/><c>+ DateTime GetNow()</c>
            /// </summary>
            public global::System.Func<global::System.DateTime>? GetNow { get; set; }

            /// <summary>
            /// Override for global::MockData.IMyClock.GetUtcNow method:<br/><c>+ DateTime GetUtcNow()</c>
            /// </summary>
            public global::System.Func<global::System.DateTime>? GetUtcNow { get; set; }

            /// <summary>
            /// Override for object.Equals method:<br/><c>+ bool Equals(object? obj)</c>
            /// </summary>
            public new global::System.Func<object?, bool>? Equals { get; set; }

            /// <summary>
            /// Override for object.GetHashCode method:<br/><c>+ int GetHashCode()</c>
            /// </summary>
            public new global::System.Func<int>? GetHashCode { get; set; }

            /// <summary>
            /// Override for object.ToString method:<br/><c>+ string? ToString()</c>
            /// </summary>
            public new global::System.Func<string?>? ToString { get; set; }

        }

        /// <summary>
        /// The unified callback invoked before each mock call.
        /// </summary>
        /// <param name="memberName">The short name of original member without generic type parameters.</param>
        partial void OnWillMockCall(string memberName);

        /// <summary>
        /// The unified callback invoked before each mock call.
        /// </summary>
        /// <param name="memberName">The short name of original member without generic type parameters.</param>
        partial void OnWillMockCall(string memberName, object?[] args);
    }
}

using System;

namespace TDoubles
{
    /// <summary>
    /// Attribute used to mark partial classes for mock generation.
    /// The source generator will create a mock implementation that delegates to the target type.
    /// </summary>
    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = false)]
    internal sealed class MockAttribute : Attribute
    {
        /// <summary>
        /// Gets the target type to be mocked.
        /// </summary>
        public Type TargetType { get; }
        
        /// <summary>
        /// Gets or sets whether to include internal members in the mock.
        /// Default is false (public members only).
        /// </summary>
        public bool IncludeInternals { get; set; } = false;

        /// <summary>
        /// Gets or sets the short names of members to exclude from the generated mock.
        /// </summary>
        public string[] ExcludeMemberShortNames { get; set; } = Array.Empty<string>();
        
        /// <summary>
        /// Initializes a new instance of the MockAttribute with the specified target type.
        /// </summary>
        /// <param name="targetType">The type to be mocked. Cannot be null.</param>
        /// <param name="excludeMemberShortNames">Optional: Short names of members to exclude from the generated mock.</param>
        /// <exception cref="ArgumentNullException">Thrown when targetType is null.</exception>
        public MockAttribute(Type targetType, params string[] excludeMemberShortNames)
        {
            TargetType = targetType ?? throw new ArgumentNullException(nameof(targetType));
            ExcludeMemberShortNames = excludeMemberShortNames ?? Array.Empty<string>();
        }
    }
}
using System;

namespace TDoubles
{
    /// <summary>
    /// 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.
    /// </summary>
    internal class TDoublesException : Exception
    {
        /// <summary>
        /// Gets the name of the member that lacks a mock implementation.
        /// </summary>
        public string MemberName { get; }
        
        /// <summary>
        /// Gets the name of the type that contains the member.
        /// </summary>
        public string TypeName { get; }
        
        /// <summary>
        /// Initializes a new instance of the TDoublesException with the specified member and type names.
        /// </summary>
        /// <param name="memberName">The name of the member that lacks a mock implementation. Cannot be null.</param>
        /// <param name="typeName">The name of the type that contains the member. Cannot be null.</param>
        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));
        }
        
        /// <summary>
        /// Initializes a new instance of the TDoublesException with the specified member name, type name, and custom message.
        /// </summary>
        /// <param name="memberName">The name of the member that lacks a mock implementation. Cannot be null.</param>
        /// <param name="typeName">The name of the type that contains the member. Cannot be null.</param>
        /// <param name="message">The custom error message.</param>
        public TDoublesException(string memberName, string typeName, string message) 
            : base(message)
        {
            MemberName = memberName ?? throw new ArgumentNullException(nameof(memberName));
            TypeName = typeName ?? throw new ArgumentNullException(nameof(typeName));
        }
    }
}

Code and pdf at

https://ignatandrei.github.io/RSCG_Examples/v2/docs/SatorImaging.TDoubles


Posted

in

,

by

Tags: