diff --git a/TUnit.Mocks.SourceGenerator.Tests/Snapshots/Interface_Inheriting_Multiple_Interfaces.verified.txt b/TUnit.Mocks.SourceGenerator.Tests/Snapshots/Interface_Inheriting_Multiple_Interfaces.verified.txt index 519639f46b..a9052e800a 100644 --- a/TUnit.Mocks.SourceGenerator.Tests/Snapshots/Interface_Inheriting_Multiple_Interfaces.verified.txt +++ b/TUnit.Mocks.SourceGenerator.Tests/Snapshots/Interface_Inheriting_Multiple_Interfaces.verified.txt @@ -75,19 +75,19 @@ namespace TUnit.Mocks.Generated public static global::TUnit.Mocks.VoidMockMethodCall Flush(this global::TUnit.Mocks.Mock mock) { var matchers = global::System.Array.Empty(); - return new global::TUnit.Mocks.VoidMockMethodCall(mock.Engine, 0, "Flush", matchers); + return new global::TUnit.Mocks.VoidMockMethodCall(global::TUnit.Mocks.Mock.GetEngine(mock), 0, "Flush", matchers); } public static global::TUnit.Mocks.MockMethodCall Read(this global::TUnit.Mocks.Mock mock) { var matchers = global::System.Array.Empty(); - return new global::TUnit.Mocks.MockMethodCall(mock.Engine, 1, "Read", matchers); + return new global::TUnit.Mocks.MockMethodCall(global::TUnit.Mocks.Mock.GetEngine(mock), 1, "Read", matchers); } public static IReadWriter_Write_M2_MockCall Write(this global::TUnit.Mocks.Mock mock, global::TUnit.Mocks.Arguments.Arg data) { var matchers = new global::TUnit.Mocks.Arguments.IArgumentMatcher[] { data.Matcher }; - return new IReadWriter_Write_M2_MockCall(mock.Engine, 2, "Write", matchers); + return new IReadWriter_Write_M2_MockCall(global::TUnit.Mocks.Mock.GetEngine(mock), 2, "Write", matchers); } } diff --git a/TUnit.Mocks.SourceGenerator.Tests/Snapshots/Interface_With_Async_Methods.verified.txt b/TUnit.Mocks.SourceGenerator.Tests/Snapshots/Interface_With_Async_Methods.verified.txt index 1518968e91..4f0d0c19f3 100644 --- a/TUnit.Mocks.SourceGenerator.Tests/Snapshots/Interface_With_Async_Methods.verified.txt +++ b/TUnit.Mocks.SourceGenerator.Tests/Snapshots/Interface_With_Async_Methods.verified.txt @@ -112,25 +112,25 @@ namespace TUnit.Mocks.Generated public static IAsyncService_GetValueAsync_M0_MockCall GetValueAsync(this global::TUnit.Mocks.Mock mock, global::TUnit.Mocks.Arguments.Arg key) { var matchers = new global::TUnit.Mocks.Arguments.IArgumentMatcher[] { key.Matcher }; - return new IAsyncService_GetValueAsync_M0_MockCall(mock.Engine, 0, "GetValueAsync", matchers); + return new IAsyncService_GetValueAsync_M0_MockCall(global::TUnit.Mocks.Mock.GetEngine(mock), 0, "GetValueAsync", matchers); } public static global::TUnit.Mocks.VoidMockMethodCall DoWorkAsync(this global::TUnit.Mocks.Mock mock) { var matchers = global::System.Array.Empty(); - return new global::TUnit.Mocks.VoidMockMethodCall(mock.Engine, 1, "DoWorkAsync", matchers); + return new global::TUnit.Mocks.VoidMockMethodCall(global::TUnit.Mocks.Mock.GetEngine(mock), 1, "DoWorkAsync", matchers); } public static IAsyncService_ComputeAsync_M2_MockCall ComputeAsync(this global::TUnit.Mocks.Mock mock, global::TUnit.Mocks.Arguments.Arg input) { var matchers = new global::TUnit.Mocks.Arguments.IArgumentMatcher[] { input.Matcher }; - return new IAsyncService_ComputeAsync_M2_MockCall(mock.Engine, 2, "ComputeAsync", matchers); + return new IAsyncService_ComputeAsync_M2_MockCall(global::TUnit.Mocks.Mock.GetEngine(mock), 2, "ComputeAsync", matchers); } public static IAsyncService_InitializeAsync_M3_MockCall InitializeAsync(this global::TUnit.Mocks.Mock mock, global::TUnit.Mocks.Arguments.Arg ct) { var matchers = new global::TUnit.Mocks.Arguments.IArgumentMatcher[] { ct.Matcher }; - return new IAsyncService_InitializeAsync_M3_MockCall(mock.Engine, 3, "InitializeAsync", matchers); + return new IAsyncService_InitializeAsync_M3_MockCall(global::TUnit.Mocks.Mock.GetEngine(mock), 3, "InitializeAsync", matchers); } } diff --git a/TUnit.Mocks.SourceGenerator.Tests/Snapshots/Interface_With_Events.verified.txt b/TUnit.Mocks.SourceGenerator.Tests/Snapshots/Interface_With_Events.verified.txt index be44e45cc5..4f8bac42ce 100644 --- a/TUnit.Mocks.SourceGenerator.Tests/Snapshots/Interface_With_Events.verified.txt +++ b/TUnit.Mocks.SourceGenerator.Tests/Snapshots/Interface_With_Events.verified.txt @@ -15,7 +15,7 @@ namespace TUnit.Mocks.Generated { extension(global::TUnit.Mocks.Mock mock) { - public INotifier_MockEvents Events => new(mock.Engine); + public INotifier_MockEvents Events => new(global::TUnit.Mocks.Mock.GetEngine(mock)); } extension(INotifier_MockEvents events) @@ -117,12 +117,12 @@ namespace TUnit.Mocks.Generated public static INotifier_Notify_M0_MockCall Notify(this global::TUnit.Mocks.Mock mock, global::TUnit.Mocks.Arguments.Arg message) { var matchers = new global::TUnit.Mocks.Arguments.IArgumentMatcher[] { message.Matcher }; - return new INotifier_Notify_M0_MockCall(mock.Engine, 0, "Notify", matchers); + return new INotifier_Notify_M0_MockCall(global::TUnit.Mocks.Mock.GetEngine(mock), 0, "Notify", matchers); } public static void RaiseItemAdded(this global::TUnit.Mocks.Mock mock, string e) { - ((global::TUnit.Mocks.IRaisable)mock.Engine.Raisable!).RaiseEvent("ItemAdded", (object?)e); + ((global::TUnit.Mocks.IRaisable)global::TUnit.Mocks.Mock.GetEngine(mock).Raisable!).RaiseEvent("ItemAdded", (object?)e); } } diff --git a/TUnit.Mocks.SourceGenerator.Tests/Snapshots/Interface_With_Generic_Methods.verified.txt b/TUnit.Mocks.SourceGenerator.Tests/Snapshots/Interface_With_Generic_Methods.verified.txt index dbf5e5a4d3..200e8989ca 100644 --- a/TUnit.Mocks.SourceGenerator.Tests/Snapshots/Interface_With_Generic_Methods.verified.txt +++ b/TUnit.Mocks.SourceGenerator.Tests/Snapshots/Interface_With_Generic_Methods.verified.txt @@ -75,19 +75,19 @@ namespace TUnit.Mocks.Generated public static global::TUnit.Mocks.MockMethodCall GetById(this global::TUnit.Mocks.Mock mock, global::TUnit.Mocks.Arguments.Arg id) where T : class { var matchers = new global::TUnit.Mocks.Arguments.IArgumentMatcher[] { id.Matcher }; - return new global::TUnit.Mocks.MockMethodCall(mock.Engine, 0, "GetById", matchers); + return new global::TUnit.Mocks.MockMethodCall(global::TUnit.Mocks.Mock.GetEngine(mock), 0, "GetById", matchers); } public static global::TUnit.Mocks.VoidMockMethodCall Save(this global::TUnit.Mocks.Mock mock, global::TUnit.Mocks.Arguments.Arg entity) where T : class, new() { var matchers = new global::TUnit.Mocks.Arguments.IArgumentMatcher[] { entity.Matcher }; - return new global::TUnit.Mocks.VoidMockMethodCall(mock.Engine, 1, "Save", matchers); + return new global::TUnit.Mocks.VoidMockMethodCall(global::TUnit.Mocks.Mock.GetEngine(mock), 1, "Save", matchers); } public static global::TUnit.Mocks.MockMethodCall Transform(this global::TUnit.Mocks.Mock mock, global::TUnit.Mocks.Arguments.Arg input) where TInput : notnull where TResult : struct { var matchers = new global::TUnit.Mocks.Arguments.IArgumentMatcher[] { input.Matcher }; - return new global::TUnit.Mocks.MockMethodCall(mock.Engine, 2, "Transform", matchers); + return new global::TUnit.Mocks.MockMethodCall(global::TUnit.Mocks.Mock.GetEngine(mock), 2, "Transform", matchers); } } } diff --git a/TUnit.Mocks.SourceGenerator.Tests/Snapshots/Interface_With_Mixed_Members.verified.txt b/TUnit.Mocks.SourceGenerator.Tests/Snapshots/Interface_With_Mixed_Members.verified.txt index 67f4b50f62..c8ea45824d 100644 --- a/TUnit.Mocks.SourceGenerator.Tests/Snapshots/Interface_With_Mixed_Members.verified.txt +++ b/TUnit.Mocks.SourceGenerator.Tests/Snapshots/Interface_With_Mixed_Members.verified.txt @@ -15,7 +15,7 @@ namespace TUnit.Mocks.Generated { extension(global::TUnit.Mocks.Mock mock) { - public IService_MockEvents Events => new(mock.Engine); + public IService_MockEvents Events => new(global::TUnit.Mocks.Mock.GetEngine(mock)); } extension(IService_MockEvents events) @@ -141,27 +141,27 @@ namespace TUnit.Mocks.Generated public static IService_GetAsync_M3_MockCall GetAsync(this global::TUnit.Mocks.Mock mock, global::TUnit.Mocks.Arguments.Arg id) { var matchers = new global::TUnit.Mocks.Arguments.IArgumentMatcher[] { id.Matcher }; - return new IService_GetAsync_M3_MockCall(mock.Engine, 3, "GetAsync", matchers); + return new IService_GetAsync_M3_MockCall(global::TUnit.Mocks.Mock.GetEngine(mock), 3, "GetAsync", matchers); } public static IService_Process_M4_MockCall Process(this global::TUnit.Mocks.Mock mock, global::TUnit.Mocks.Arguments.Arg data) { var matchers = new global::TUnit.Mocks.Arguments.IArgumentMatcher[] { data.Matcher }; - return new IService_Process_M4_MockCall(mock.Engine, 4, "Process", matchers); + return new IService_Process_M4_MockCall(global::TUnit.Mocks.Mock.GetEngine(mock), 4, "Process", matchers); } extension(global::TUnit.Mocks.Mock mock) { public global::TUnit.Mocks.PropertyMockCall Name - => new(mock.Engine, 0, 1, "Name", true, true); + => new(global::TUnit.Mocks.Mock.GetEngine(mock), 0, 1, "Name", true, true); public global::TUnit.Mocks.PropertyMockCall Count - => new(mock.Engine, 2, 0, "Count", true, false); + => new(global::TUnit.Mocks.Mock.GetEngine(mock), 2, 0, "Count", true, false); } public static void RaiseStatusChanged(this global::TUnit.Mocks.Mock mock, string e) { - ((global::TUnit.Mocks.IRaisable)mock.Engine.Raisable!).RaiseEvent("StatusChanged", (object?)e); + ((global::TUnit.Mocks.IRaisable)global::TUnit.Mocks.Mock.GetEngine(mock).Raisable!).RaiseEvent("StatusChanged", (object?)e); } } diff --git a/TUnit.Mocks.SourceGenerator.Tests/Snapshots/Interface_With_Out_Ref_Parameters.verified.txt b/TUnit.Mocks.SourceGenerator.Tests/Snapshots/Interface_With_Out_Ref_Parameters.verified.txt index 1c80691d99..54ea3591ec 100644 --- a/TUnit.Mocks.SourceGenerator.Tests/Snapshots/Interface_With_Out_Ref_Parameters.verified.txt +++ b/TUnit.Mocks.SourceGenerator.Tests/Snapshots/Interface_With_Out_Ref_Parameters.verified.txt @@ -1,4 +1,4 @@ -// +// #nullable enable namespace TUnit.Mocks.Generated @@ -83,13 +83,13 @@ namespace TUnit.Mocks.Generated public static IDictionary_TryGetValue_M0_MockCall TryGetValue(this global::TUnit.Mocks.Mock mock, global::TUnit.Mocks.Arguments.Arg key) { var matchers = new global::TUnit.Mocks.Arguments.IArgumentMatcher[] { key.Matcher }; - return new IDictionary_TryGetValue_M0_MockCall(mock.Engine, 0, "TryGetValue", matchers); + return new IDictionary_TryGetValue_M0_MockCall(global::TUnit.Mocks.Mock.GetEngine(mock), 0, "TryGetValue", matchers); } public static IDictionary_Swap_M1_MockCall Swap(this global::TUnit.Mocks.Mock mock, global::TUnit.Mocks.Arguments.Arg a, global::TUnit.Mocks.Arguments.Arg b) { var matchers = new global::TUnit.Mocks.Arguments.IArgumentMatcher[] { a.Matcher, b.Matcher }; - return new IDictionary_Swap_M1_MockCall(mock.Engine, 1, "Swap", matchers); + return new IDictionary_Swap_M1_MockCall(global::TUnit.Mocks.Mock.GetEngine(mock), 1, "Swap", matchers); } } diff --git a/TUnit.Mocks.SourceGenerator.Tests/Snapshots/Interface_With_Overloaded_Methods.verified.txt b/TUnit.Mocks.SourceGenerator.Tests/Snapshots/Interface_With_Overloaded_Methods.verified.txt index 57c183c550..3ff1a23f23 100644 --- a/TUnit.Mocks.SourceGenerator.Tests/Snapshots/Interface_With_Overloaded_Methods.verified.txt +++ b/TUnit.Mocks.SourceGenerator.Tests/Snapshots/Interface_With_Overloaded_Methods.verified.txt @@ -80,25 +80,25 @@ namespace TUnit.Mocks.Generated public static IFormatter_Format_M0_MockCall Format(this global::TUnit.Mocks.Mock mock, global::TUnit.Mocks.Arguments.Arg value) { var matchers = new global::TUnit.Mocks.Arguments.IArgumentMatcher[] { value.Matcher }; - return new IFormatter_Format_M0_MockCall(mock.Engine, 0, "Format", matchers); + return new IFormatter_Format_M0_MockCall(global::TUnit.Mocks.Mock.GetEngine(mock), 0, "Format", matchers); } public static IFormatter_Format_M1_MockCall Format(this global::TUnit.Mocks.Mock mock, global::TUnit.Mocks.Arguments.Arg value) { var matchers = new global::TUnit.Mocks.Arguments.IArgumentMatcher[] { value.Matcher }; - return new IFormatter_Format_M1_MockCall(mock.Engine, 1, "Format", matchers); + return new IFormatter_Format_M1_MockCall(global::TUnit.Mocks.Mock.GetEngine(mock), 1, "Format", matchers); } public static IFormatter_Format_M2_MockCall Format(this global::TUnit.Mocks.Mock mock, global::TUnit.Mocks.Arguments.Arg template, global::TUnit.Mocks.Arguments.Arg arg1) { var matchers = new global::TUnit.Mocks.Arguments.IArgumentMatcher[] { template.Matcher, arg1.Matcher }; - return new IFormatter_Format_M2_MockCall(mock.Engine, 2, "Format", matchers); + return new IFormatter_Format_M2_MockCall(global::TUnit.Mocks.Mock.GetEngine(mock), 2, "Format", matchers); } public static IFormatter_Format_M3_MockCall Format(this global::TUnit.Mocks.Mock mock, global::TUnit.Mocks.Arguments.Arg template, global::TUnit.Mocks.Arguments.Arg arg1, global::TUnit.Mocks.Arguments.Arg arg2) { var matchers = new global::TUnit.Mocks.Arguments.IArgumentMatcher[] { template.Matcher, arg1.Matcher, arg2.Matcher }; - return new IFormatter_Format_M3_MockCall(mock.Engine, 3, "Format", matchers); + return new IFormatter_Format_M3_MockCall(global::TUnit.Mocks.Mock.GetEngine(mock), 3, "Format", matchers); } } diff --git a/TUnit.Mocks.SourceGenerator.Tests/Snapshots/Interface_With_Properties.verified.txt b/TUnit.Mocks.SourceGenerator.Tests/Snapshots/Interface_With_Properties.verified.txt index a40e899791..fdd42f5115 100644 --- a/TUnit.Mocks.SourceGenerator.Tests/Snapshots/Interface_With_Properties.verified.txt +++ b/TUnit.Mocks.SourceGenerator.Tests/Snapshots/Interface_With_Properties.verified.txt @@ -76,13 +76,13 @@ namespace TUnit.Mocks.Generated extension(global::TUnit.Mocks.Mock mock) { public global::TUnit.Mocks.PropertyMockCall Name - => new(mock.Engine, 0, 1, "Name", true, true); + => new(global::TUnit.Mocks.Mock.GetEngine(mock), 0, 1, "Name", true, true); public global::TUnit.Mocks.PropertyMockCall Count - => new(mock.Engine, 2, 0, "Count", true, false); + => new(global::TUnit.Mocks.Mock.GetEngine(mock), 2, 0, "Count", true, false); public global::TUnit.Mocks.PropertyMockCall IsOpen - => new(mock.Engine, 0, 4, "IsOpen", false, true); + => new(global::TUnit.Mocks.Mock.GetEngine(mock), 0, 4, "IsOpen", false, true); } } } diff --git a/TUnit.Mocks.SourceGenerator.Tests/Snapshots/Interface_With_RefStruct_Parameters.verified.txt b/TUnit.Mocks.SourceGenerator.Tests/Snapshots/Interface_With_RefStruct_Parameters.verified.txt index 7e9308795d..85bbb1d4db 100644 --- a/TUnit.Mocks.SourceGenerator.Tests/Snapshots/Interface_With_RefStruct_Parameters.verified.txt +++ b/TUnit.Mocks.SourceGenerator.Tests/Snapshots/Interface_With_RefStruct_Parameters.verified.txt @@ -86,13 +86,13 @@ namespace TUnit.Mocks.Generated public static global::TUnit.Mocks.VoidMockMethodCall Process(this global::TUnit.Mocks.Mock mock, global::TUnit.Mocks.Arguments.RefStructArg> data) { var matchers = new global::TUnit.Mocks.Arguments.IArgumentMatcher[] { data.Matcher }; - return new global::TUnit.Mocks.VoidMockMethodCall(mock.Engine, 0, "Process", matchers); + return new global::TUnit.Mocks.VoidMockMethodCall(global::TUnit.Mocks.Mock.GetEngine(mock), 0, "Process", matchers); } #else public static global::TUnit.Mocks.VoidMockMethodCall Process(this global::TUnit.Mocks.Mock mock) { var matchers = global::System.Array.Empty(); - return new global::TUnit.Mocks.VoidMockMethodCall(mock.Engine, 0, "Process", matchers); + return new global::TUnit.Mocks.VoidMockMethodCall(global::TUnit.Mocks.Mock.GetEngine(mock), 0, "Process", matchers); } #endif @@ -100,20 +100,20 @@ namespace TUnit.Mocks.Generated public static global::TUnit.Mocks.MockMethodCall Parse(this global::TUnit.Mocks.Mock mock, global::TUnit.Mocks.Arguments.RefStructArg> text) { var matchers = new global::TUnit.Mocks.Arguments.IArgumentMatcher[] { text.Matcher }; - return new global::TUnit.Mocks.MockMethodCall(mock.Engine, 1, "Parse", matchers); + return new global::TUnit.Mocks.MockMethodCall(global::TUnit.Mocks.Mock.GetEngine(mock), 1, "Parse", matchers); } #else public static global::TUnit.Mocks.MockMethodCall Parse(this global::TUnit.Mocks.Mock mock) { var matchers = global::System.Array.Empty(); - return new global::TUnit.Mocks.MockMethodCall(mock.Engine, 1, "Parse", matchers); + return new global::TUnit.Mocks.MockMethodCall(global::TUnit.Mocks.Mock.GetEngine(mock), 1, "Parse", matchers); } #endif public static global::TUnit.Mocks.MockMethodCall GetName(this global::TUnit.Mocks.Mock mock) { var matchers = global::System.Array.Empty(); - return new global::TUnit.Mocks.MockMethodCall(mock.Engine, 2, "GetName", matchers); + return new global::TUnit.Mocks.MockMethodCall(global::TUnit.Mocks.Mock.GetEngine(mock), 2, "GetName", matchers); } } } diff --git a/TUnit.Mocks.SourceGenerator.Tests/Snapshots/Multi_Method_Interface.verified.txt b/TUnit.Mocks.SourceGenerator.Tests/Snapshots/Multi_Method_Interface.verified.txt index e821bafd8c..27477809c6 100644 --- a/TUnit.Mocks.SourceGenerator.Tests/Snapshots/Multi_Method_Interface.verified.txt +++ b/TUnit.Mocks.SourceGenerator.Tests/Snapshots/Multi_Method_Interface.verified.txt @@ -75,19 +75,19 @@ namespace TUnit.Mocks.Generated public static ICalculator_Add_M0_MockCall Add(this global::TUnit.Mocks.Mock mock, global::TUnit.Mocks.Arguments.Arg a, global::TUnit.Mocks.Arguments.Arg b) { var matchers = new global::TUnit.Mocks.Arguments.IArgumentMatcher[] { a.Matcher, b.Matcher }; - return new ICalculator_Add_M0_MockCall(mock.Engine, 0, "Add", matchers); + return new ICalculator_Add_M0_MockCall(global::TUnit.Mocks.Mock.GetEngine(mock), 0, "Add", matchers); } public static ICalculator_Subtract_M1_MockCall Subtract(this global::TUnit.Mocks.Mock mock, global::TUnit.Mocks.Arguments.Arg a, global::TUnit.Mocks.Arguments.Arg b) { var matchers = new global::TUnit.Mocks.Arguments.IArgumentMatcher[] { a.Matcher, b.Matcher }; - return new ICalculator_Subtract_M1_MockCall(mock.Engine, 1, "Subtract", matchers); + return new ICalculator_Subtract_M1_MockCall(global::TUnit.Mocks.Mock.GetEngine(mock), 1, "Subtract", matchers); } - public static global::TUnit.Mocks.VoidMockMethodCall Reset_(this global::TUnit.Mocks.Mock mock) + public static global::TUnit.Mocks.VoidMockMethodCall Reset(this global::TUnit.Mocks.Mock mock) { var matchers = global::System.Array.Empty(); - return new global::TUnit.Mocks.VoidMockMethodCall(mock.Engine, 2, "Reset", matchers); + return new global::TUnit.Mocks.VoidMockMethodCall(global::TUnit.Mocks.Mock.GetEngine(mock), 2, "Reset", matchers); } } diff --git a/TUnit.Mocks.SourceGenerator.Tests/Snapshots/Simple_Interface_With_One_Method.verified.txt b/TUnit.Mocks.SourceGenerator.Tests/Snapshots/Simple_Interface_With_One_Method.verified.txt index cfb21aec13..b054d42815 100644 --- a/TUnit.Mocks.SourceGenerator.Tests/Snapshots/Simple_Interface_With_One_Method.verified.txt +++ b/TUnit.Mocks.SourceGenerator.Tests/Snapshots/Simple_Interface_With_One_Method.verified.txt @@ -65,7 +65,7 @@ namespace TUnit.Mocks.Generated public static IGreeter_Greet_M0_MockCall Greet(this global::TUnit.Mocks.Mock mock, global::TUnit.Mocks.Arguments.Arg name) { var matchers = new global::TUnit.Mocks.Arguments.IArgumentMatcher[] { name.Matcher }; - return new IGreeter_Greet_M0_MockCall(mock.Engine, 0, "Greet", matchers); + return new IGreeter_Greet_M0_MockCall(global::TUnit.Mocks.Mock.GetEngine(mock), 0, "Greet", matchers); } } diff --git a/TUnit.Mocks.SourceGenerator/Builders/MockEventsBuilder.cs b/TUnit.Mocks.SourceGenerator/Builders/MockEventsBuilder.cs index 2a5c11b06a..6ce8250eb3 100644 --- a/TUnit.Mocks.SourceGenerator/Builders/MockEventsBuilder.cs +++ b/TUnit.Mocks.SourceGenerator/Builders/MockEventsBuilder.cs @@ -31,7 +31,7 @@ public static string Build(MockTypeModel model) // Extension property on Mock — non-nullable, only present when type has events using (writer.Block($"extension(global::TUnit.Mocks.Mock<{model.FullyQualifiedName}> mock)")) { - writer.AppendLine($"public {safeName}_MockEvents Events => new(mock.Engine);"); + writer.AppendLine($"public {safeName}_MockEvents Events => new(global::TUnit.Mocks.Mock.GetEngine(mock));"); } writer.AppendLine(); diff --git a/TUnit.Mocks.SourceGenerator/Builders/MockMembersBuilder.cs b/TUnit.Mocks.SourceGenerator/Builders/MockMembersBuilder.cs index 362b94ef9b..d2a613bf6b 100644 --- a/TUnit.Mocks.SourceGenerator/Builders/MockMembersBuilder.cs +++ b/TUnit.Mocks.SourceGenerator/Builders/MockMembersBuilder.cs @@ -16,9 +16,7 @@ internal static class MockMembersBuilder private static readonly HashSet MockMemberNames = new(System.StringComparer.Ordinal) { - "Object", "Engine", "Behavior", "Invocations", "DefaultValueProvider", - "SetupAllProperties", "Reset", "VerifyAll", "VerifyNoOtherCalls", - "GetAutoMock", "GetDiagnostics", "SetState", "InState", + "Object", "GetHashCode", "GetType", "ToString", "Equals" }; @@ -557,15 +555,15 @@ private static void EmitMemberMethodBody(CodeWriter writer, MockMemberModel meth if (useTypedWrapper) { var wrapperName = GetWrapperName(safeName, method); - writer.AppendLine($"return new {wrapperName}(mock.Engine, {method.MemberId}, \"{method.Name}\", matchers);"); + writer.AppendLine($"return new {wrapperName}(global::TUnit.Mocks.Mock.GetEngine(mock), {method.MemberId}, \"{method.Name}\", matchers);"); } else if (method.IsVoid || method.IsRefStructReturn) { - writer.AppendLine($"return new global::TUnit.Mocks.VoidMockMethodCall(mock.Engine, {method.MemberId}, \"{method.Name}\", matchers);"); + writer.AppendLine($"return new global::TUnit.Mocks.VoidMockMethodCall(global::TUnit.Mocks.Mock.GetEngine(mock), {method.MemberId}, \"{method.Name}\", matchers);"); } else { - writer.AppendLine($"return new global::TUnit.Mocks.MockMethodCall<{setupReturnType}>(mock.Engine, {method.MemberId}, \"{method.Name}\", matchers);"); + writer.AppendLine($"return new global::TUnit.Mocks.MockMethodCall<{setupReturnType}>(global::TUnit.Mocks.Mock.GetEngine(mock), {method.MemberId}, \"{method.Name}\", matchers);"); } } } @@ -588,7 +586,7 @@ private static void GeneratePropertyExtensionBlock(CodeWriter writer, List {safePropName}"); - writer.AppendLine($" => new(mock.Engine, {getterMemberId}, {setterMemberId}, \"{prop.Name}\", {hasGetter}, {hasSetter});"); + writer.AppendLine($" => new(global::TUnit.Mocks.Mock.GetEngine(mock), {getterMemberId}, {setterMemberId}, \"{prop.Name}\", {hasGetter}, {hasSetter});"); } } } @@ -627,7 +625,7 @@ private static void GenerateRaiseExtensionMethods(CodeWriter writer, MockTypeMod using (writer.Block($"public static void Raise{evt.Name}({raiseParams})")) { - writer.AppendLine($"((global::TUnit.Mocks.IRaisable)mock.Engine.Raisable!).RaiseEvent(\"{evt.Name}\", {argsExpr});"); + writer.AppendLine($"((global::TUnit.Mocks.IRaisable)global::TUnit.Mocks.Mock.GetEngine(mock).Raisable!).RaiseEvent(\"{evt.Name}\", {argsExpr});"); } } } diff --git a/TUnit.Mocks.Tests/AdditionalCoverageTests.cs b/TUnit.Mocks.Tests/AdditionalCoverageTests.cs index c0c33427ac..973a1cf87b 100644 --- a/TUnit.Mocks.Tests/AdditionalCoverageTests.cs +++ b/TUnit.Mocks.Tests/AdditionalCoverageTests.cs @@ -178,23 +178,22 @@ public async Task IMock_ObjectInstance_Returns_Same_As_Object() } } -// ─── GetAutoMock Error Path ───────────────────────────────────────────────── +// ─── Mock.Get Error Path ──────────────────────────────────────────────────── -public class AutoMockErrorPathTests +public class MockGetErrorPathTests { [Test] - public async Task GetAutoMock_Throws_When_No_AutoMock_Exists() + public async Task Mock_Get_Throws_For_Non_Mock_Object() { - var mock = Mock.Of(); + // A plain object, not created by Mock.Of + var notAMock = new List(); - // No method has been called, so no auto-mock was created var ex = Assert.Throws(() => { - mock.GetAutoMock("NonExistentMethod"); + Mock.Get(notAMock); }); - await Assert.That(ex.Message).Contains("No auto-mock found"); - await Assert.That(ex.Message).Contains("NonExistentMethod"); + await Assert.That(ex.Message).Contains("is not a mock"); } } @@ -212,7 +211,7 @@ public async Task VerifyAll_Message_Includes_Matcher_Descriptions() // Act — don't call the method // Assert - var ex = Assert.Throws(() => mock.VerifyAll()); + var ex = Assert.Throws(() => Mock.VerifyAll(mock)); await Assert.That(ex.Message).Contains("Add("); await Assert.That(ex.Message).Contains("never invoked"); } @@ -229,7 +228,7 @@ public async Task VerifyAll_Message_Lists_Multiple_Uninvoked_Setups() // Act — don't call any methods // Assert - var ex = Assert.Throws(() => mock.VerifyAll()); + var ex = Assert.Throws(() => Mock.VerifyAll(mock)); await Assert.That(ex.Message).Contains("Add("); await Assert.That(ex.Message).Contains("GetName()"); await Assert.That(ex.Message).Contains("Log("); @@ -248,7 +247,7 @@ public async Task VerifyAll_Passes_When_All_Setups_Invoked() mock.Object.GetName(); // Assert — no exception - mock.VerifyAll(); + Mock.VerifyAll(mock); await Assert.That(true).IsTrue(); } } @@ -267,11 +266,12 @@ public async Task Invocations_Are_In_Call_Order() mock.Object.GetName(); mock.Object.Add(3, 4); - await Assert.That(mock.Invocations).HasCount().EqualTo(4); - await Assert.That(mock.Invocations[0].MemberName).IsEqualTo("Add"); - await Assert.That(mock.Invocations[1].MemberName).IsEqualTo("Log"); - await Assert.That(mock.Invocations[2].MemberName).IsEqualTo("GetName"); - await Assert.That(mock.Invocations[3].MemberName).IsEqualTo("Add"); + var invocations = Mock.GetInvocations(mock); + await Assert.That(invocations).HasCount().EqualTo(4); + await Assert.That(invocations[0].MemberName).IsEqualTo("Add"); + await Assert.That(invocations[1].MemberName).IsEqualTo("Log"); + await Assert.That(invocations[2].MemberName).IsEqualTo("GetName"); + await Assert.That(invocations[3].MemberName).IsEqualTo("Add"); } [Test] @@ -283,10 +283,11 @@ public async Task Invocations_Sequence_Numbers_Are_Monotonically_Increasing() mock.Object.GetName(); mock.Object.Log("msg"); - for (int i = 1; i < mock.Invocations.Count; i++) + var invocations = Mock.GetInvocations(mock); + for (int i = 1; i < invocations.Count; i++) { - await Assert.That(mock.Invocations[i].SequenceNumber) - .IsGreaterThan(mock.Invocations[i - 1].SequenceNumber); + await Assert.That(invocations[i].SequenceNumber) + .IsGreaterThan(invocations[i - 1].SequenceNumber); } } } @@ -305,7 +306,7 @@ public class AutoTrackPropertyResetTests public async Task Reset_Clears_Auto_Tracked_Property_Values() { var mock = Mock.Of(); - mock.SetupAllProperties(); + Mock.SetupAllProperties(mock); var svc = mock.Object; svc.Theme = "dark"; @@ -315,8 +316,8 @@ public async Task Reset_Clears_Auto_Tracked_Property_Values() await Assert.That(svc.FontSize).IsEqualTo(14); // Reset should clear tracked values - mock.Reset(); - mock.SetupAllProperties(); + Mock.Reset(mock); + Mock.SetupAllProperties(mock); // After reset + re-enable auto-track, values are back to defaults await Assert.That(svc.Theme).IsNotEqualTo("dark"); @@ -373,9 +374,9 @@ public async Task DefaultValueProvider_Get_Set_Roundtrip() var mock = Mock.Of(); var provider = new FixedStringProvider(); - mock.DefaultValueProvider = provider; + Mock.SetDefaultValueProvider(mock, provider); - await Assert.That(mock.DefaultValueProvider).IsEqualTo(provider); + await Assert.That(Mock.GetDefaultValueProvider(mock)).IsEqualTo(provider); } [Test] @@ -398,13 +399,13 @@ public class MockBehaviorPropertyTests public async Task Loose_Mock_Has_Loose_Behavior() { var mock = Mock.Of(); - await Assert.That(mock.Behavior).IsEqualTo(MockBehavior.Loose); + await Assert.That(Mock.GetBehavior(mock)).IsEqualTo(MockBehavior.Loose); } [Test] public async Task Strict_Mock_Has_Strict_Behavior() { var mock = Mock.Of(MockBehavior.Strict); - await Assert.That(mock.Behavior).IsEqualTo(MockBehavior.Strict); + await Assert.That(Mock.GetBehavior(mock)).IsEqualTo(MockBehavior.Strict); } } diff --git a/TUnit.Mocks.Tests/AutoMockTests.cs b/TUnit.Mocks.Tests/AutoMockTests.cs index 2908f18212..c512cb7127 100644 --- a/TUnit.Mocks.Tests/AutoMockTests.cs +++ b/TUnit.Mocks.Tests/AutoMockTests.cs @@ -122,16 +122,14 @@ public async Task Nested_Chain_Returns_Default_Values() } [Test] - public async Task Auto_Mock_Configurable_Via_GetAutoMock() + public async Task Auto_Mock_Configurable_Via_Mock_Get() { // Arrange var mock = Mock.Of(); - // Trigger auto-mock creation + // Trigger auto-mock creation and retrieve the wrapper var serviceB = mock.Object.GetServiceB(); - - // Retrieve and configure the auto-mock - var autoMock = mock.GetAutoMock("GetServiceB"); + var autoMock = Mock.Get(serviceB); autoMock.GetValue().Returns(42); // Act diff --git a/TUnit.Mocks.Tests/AutoTrackPropertyTests.cs b/TUnit.Mocks.Tests/AutoTrackPropertyTests.cs index 543f1d203a..d2be1da16a 100644 --- a/TUnit.Mocks.Tests/AutoTrackPropertyTests.cs +++ b/TUnit.Mocks.Tests/AutoTrackPropertyTests.cs @@ -23,7 +23,7 @@ public async Task AutoTrack_Set_Then_Get_Returns_Value() { // Arrange — opt in to auto-tracking var mock = Mock.Of(); - mock.SetupAllProperties(); + Mock.SetupAllProperties(mock); // Act mock.Object.Name = "Alice"; @@ -38,7 +38,7 @@ public async Task AutoTrack_Multiple_Properties_Track_Independently() { // Arrange var mock = Mock.Of(); - mock.SetupAllProperties(); + Mock.SetupAllProperties(mock); // Act mock.Object.Name = "Bob"; @@ -68,7 +68,7 @@ public async Task Explicit_Setup_Overrides_AutoTrack() { // Arrange var mock = Mock.Of(); - mock.SetupAllProperties(); + Mock.SetupAllProperties(mock); mock.Name.Returns("Configured"); // Act — set a tracked value, but explicit setup should win @@ -83,7 +83,7 @@ public async Task AutoTrack_Overwrite_Value() { // Arrange var mock = Mock.Of(); - mock.SetupAllProperties(); + Mock.SetupAllProperties(mock); // Act — set then overwrite mock.Object.Name = "First"; @@ -111,7 +111,7 @@ public async Task Loose_Mode_AutoTracks_After_SetupAllProperties() { // Arrange — explicit opt-in enables auto-tracking var mock = Mock.Of(); - mock.SetupAllProperties(); + Mock.SetupAllProperties(mock); // Act mock.Object.Name = "Alice"; @@ -140,7 +140,7 @@ public async Task Strict_Mode_With_SetupAllProperties_Tracks() { // Arrange — strict mode with explicit opt-in var mock = Mock.Of(MockBehavior.Strict); - mock.SetupAllProperties(); + Mock.SetupAllProperties(mock); mock.Name.Set(Arg.Any()); mock.Name.Returns(""); @@ -156,11 +156,11 @@ public async Task Reset_Clears_Tracked_Values_But_Keeps_AutoTrack() { // Arrange var mock = Mock.Of(); - mock.SetupAllProperties(); + Mock.SetupAllProperties(mock); mock.Object.Name = "Alice"; // Act - mock.Reset(); + Mock.Reset(mock); // Assert — tracked values cleared, but auto-track still active await Assert.That(mock.Object.Name).IsEmpty(); diff --git a/TUnit.Mocks.Tests/BasicMockTests.cs b/TUnit.Mocks.Tests/BasicMockTests.cs index e28facf8d0..8a07da3478 100644 --- a/TUnit.Mocks.Tests/BasicMockTests.cs +++ b/TUnit.Mocks.Tests/BasicMockTests.cs @@ -160,7 +160,7 @@ public async Task Reset_Clears_Setups() await Assert.That(calc.Add(1, 1)).IsEqualTo(42); // Act - mock.Reset(); + Mock.Reset(mock); // Assert — after reset, returns default await Assert.That(calc.Add(1, 1)).IsEqualTo(0); diff --git a/TUnit.Mocks.Tests/DefaultValueProviderTests.cs b/TUnit.Mocks.Tests/DefaultValueProviderTests.cs index fe16f34702..58adcd6b08 100644 --- a/TUnit.Mocks.Tests/DefaultValueProviderTests.cs +++ b/TUnit.Mocks.Tests/DefaultValueProviderTests.cs @@ -28,7 +28,7 @@ public async Task Custom_Provider_Returns_String_Default() { // Arrange var mock = Mock.Of(); - mock.DefaultValueProvider = new CustomProvider(); + Mock.SetDefaultValueProvider(mock, new CustomProvider()); IGreeter greeter = mock.Object; @@ -44,7 +44,7 @@ public async Task Custom_Provider_Returns_Int_Default() { // Arrange var mock = Mock.Of(); - mock.DefaultValueProvider = new CustomProvider(); + Mock.SetDefaultValueProvider(mock, new CustomProvider()); ICalculator calc = mock.Object; @@ -60,7 +60,7 @@ public async Task Setup_Takes_Precedence_Over_Provider() { // Arrange var mock = Mock.Of(); - mock.DefaultValueProvider = new CustomProvider(); + Mock.SetDefaultValueProvider(mock, new CustomProvider()); mock.Add(1, 2).Returns(100); ICalculator calc = mock.Object; @@ -92,7 +92,7 @@ public async Task BuiltIn_Provider_Returns_Empty_String() { // Arrange var mock = Mock.Of(); - mock.DefaultValueProvider = DefaultValueProvider.Instance; + Mock.SetDefaultValueProvider(mock, DefaultValueProvider.Instance); IGreeter greeter = mock.Object; diff --git a/TUnit.Mocks.Tests/DiagnosticsTests.cs b/TUnit.Mocks.Tests/DiagnosticsTests.cs index aaec7c574b..b9d98a6f16 100644 --- a/TUnit.Mocks.Tests/DiagnosticsTests.cs +++ b/TUnit.Mocks.Tests/DiagnosticsTests.cs @@ -16,7 +16,7 @@ public async Task Unused_Setups_Detected() ICalculator calc = mock.Object; _ = calc.Add(1, 2); // only exercise the first setup - var diag = mock.GetDiagnostics(); + var diag = Mock.GetDiagnostics(mock); await Assert.That(diag.TotalSetups).IsEqualTo(2); await Assert.That(diag.ExercisedSetups).IsEqualTo(1); @@ -36,7 +36,7 @@ public async Task Unmatched_Calls_Detected() _ = calc.Add(1, 2); // matches setup _ = calc.Add(5, 5); // no setup match — unmatched - var diag = mock.GetDiagnostics(); + var diag = Mock.GetDiagnostics(mock); await Assert.That(diag.UnmatchedCalls).HasCount().EqualTo(1); await Assert.That(diag.UnmatchedCalls[0].MemberName).IsEqualTo("Add"); @@ -52,7 +52,7 @@ public async Task All_Setups_Exercised() ICalculator calc = mock.Object; _ = calc.Add(1, 2); - var diag = mock.GetDiagnostics(); + var diag = Mock.GetDiagnostics(mock); await Assert.That(diag.TotalSetups).IsEqualTo(1); await Assert.That(diag.ExercisedSetups).IsEqualTo(1); @@ -67,7 +67,7 @@ public async Task No_Calls_Means_All_Setups_Unused() mock.Add(1, 2).Returns(3); mock.Add(3, 4).Returns(7); - var diag = mock.GetDiagnostics(); + var diag = Mock.GetDiagnostics(mock); await Assert.That(diag.TotalSetups).IsEqualTo(2); await Assert.That(diag.ExercisedSetups).IsEqualTo(0); @@ -80,7 +80,7 @@ public async Task Matcher_Descriptions_Populated() var mock = Mock.Of(); mock.Add(Arg.Any(), Arg.Is(x => x > 0)).Returns(1); - var diag = mock.GetDiagnostics(); + var diag = Mock.GetDiagnostics(mock); await Assert.That(diag.UnusedSetups).HasCount().EqualTo(1); var setup = diag.UnusedSetups[0]; @@ -98,9 +98,9 @@ public async Task Reset_Clears_Diagnostics() ICalculator calc = mock.Object; _ = calc.Add(5, 5); // unmatched - mock.Reset(); + Mock.Reset(mock); - var diag = mock.GetDiagnostics(); + var diag = Mock.GetDiagnostics(mock); await Assert.That(diag.TotalSetups).IsEqualTo(0); await Assert.That(diag.ExercisedSetups).IsEqualTo(0); @@ -113,7 +113,7 @@ public async Task Empty_Mock_Has_Clean_Diagnostics() { var mock = Mock.Of(); - var diag = mock.GetDiagnostics(); + var diag = Mock.GetDiagnostics(mock); await Assert.That(diag.TotalSetups).IsEqualTo(0); await Assert.That(diag.ExercisedSetups).IsEqualTo(0); diff --git a/TUnit.Mocks.Tests/EdgeCaseTests.cs b/TUnit.Mocks.Tests/EdgeCaseTests.cs index bf1eca5076..651a718ae0 100644 --- a/TUnit.Mocks.Tests/EdgeCaseTests.cs +++ b/TUnit.Mocks.Tests/EdgeCaseTests.cs @@ -582,7 +582,7 @@ public async Task Reset_Then_Reconfigure_Different_Behavior() await Assert.That(firstResult).IsEqualTo("A"); // Reset and reconfigure - mock.Reset(); + Mock.Reset(mock); mock.GetConfig("key").Returns("B"); // Second phase @@ -609,7 +609,7 @@ public async Task Reset_Mid_Test_Changes_Verification_Baseline() mock.GetConfig("key2").WasCalled(Times.Once); // Reset — clears call history - mock.Reset(); + Mock.Reset(mock); // Post-reset: previous calls should not count mock.GetConfig("key1").WasNeverCalled(); diff --git a/TUnit.Mocks.Tests/EventSubscriptionSetupTests.cs b/TUnit.Mocks.Tests/EventSubscriptionSetupTests.cs index 3f2c6835d6..4099cd0c66 100644 --- a/TUnit.Mocks.Tests/EventSubscriptionSetupTests.cs +++ b/TUnit.Mocks.Tests/EventSubscriptionSetupTests.cs @@ -87,7 +87,7 @@ public async Task Reset_Clears_Subscription_Callbacks() var callbackFired = false; mock.Events.DataReady.OnSubscribe(() => callbackFired = true); - mock.Reset(); + Mock.Reset(mock); mock.Object.DataReady += (sender, args) => { }; diff --git a/TUnit.Mocks.Tests/EventSubscriptionVerifyTests.cs b/TUnit.Mocks.Tests/EventSubscriptionVerifyTests.cs index 3ff007ccb7..9d2332b9ef 100644 --- a/TUnit.Mocks.Tests/EventSubscriptionVerifyTests.cs +++ b/TUnit.Mocks.Tests/EventSubscriptionVerifyTests.cs @@ -83,7 +83,7 @@ public async Task Reset_Clears_Subscription_History() mock.Object.OnStringAction += _ => { }; // Act - mock.Reset(); + Mock.Reset(mock); // Assert await Assert.That(mock.Events.OnStringAction.WasSubscribed).IsFalse(); diff --git a/TUnit.Mocks.Tests/InvocationsTests.cs b/TUnit.Mocks.Tests/InvocationsTests.cs index f399c6311b..0e5ae4d433 100644 --- a/TUnit.Mocks.Tests/InvocationsTests.cs +++ b/TUnit.Mocks.Tests/InvocationsTests.cs @@ -19,7 +19,7 @@ public async Task Invocations_Returns_All_Calls() svc.GetValue("key2"); svc.Process(42); - await Assert.That(mock.Invocations.Count).IsEqualTo(3); + await Assert.That(Mock.GetInvocations(mock).Count).IsEqualTo(3); } [Test] @@ -32,8 +32,9 @@ public async Task Invocations_Contains_Correct_Method_Names() svc.GetValue("key1"); svc.Process(99); - await Assert.That(mock.Invocations[0].MemberName).IsEqualTo("GetValue"); - await Assert.That(mock.Invocations[1].MemberName).IsEqualTo("Process"); + var invocations = Mock.GetInvocations(mock); + await Assert.That(invocations[0].MemberName).IsEqualTo("GetValue"); + await Assert.That(invocations[1].MemberName).IsEqualTo("Process"); } [Test] @@ -45,14 +46,14 @@ public async Task Invocations_Contains_Correct_Arguments() var svc = mock.Object; svc.GetValue("hello"); - await Assert.That(mock.Invocations[0].Arguments[0]).IsEqualTo("hello"); + await Assert.That(Mock.GetInvocations(mock)[0].Arguments[0]).IsEqualTo("hello"); } [Test] public async Task Invocations_Is_Empty_When_No_Calls_Made() { var mock = Mock.Of(); - await Assert.That(mock.Invocations.Count).IsEqualTo(0); + await Assert.That(Mock.GetInvocations(mock).Count).IsEqualTo(0); } [Test] @@ -64,8 +65,8 @@ public async Task Invocations_Is_Empty_After_Reset() var svc = mock.Object; svc.GetValue("key1"); - mock.Reset(); + Mock.Reset(mock); - await Assert.That(mock.Invocations.Count).IsEqualTo(0); + await Assert.That(Mock.GetInvocations(mock).Count).IsEqualTo(0); } } diff --git a/TUnit.Mocks.Tests/MockRepositoryTests.cs b/TUnit.Mocks.Tests/MockRepositoryTests.cs index 61b238edd4..fc2025cf65 100644 --- a/TUnit.Mocks.Tests/MockRepositoryTests.cs +++ b/TUnit.Mocks.Tests/MockRepositoryTests.cs @@ -137,8 +137,8 @@ public async Task Repository_Reset_Clears_All_Mocks() // Assert — setups and history are cleared await Assert.That(serviceMock.Object.GetData(1)).IsEmpty(); // no setup, returns smart default - await Assert.That(serviceMock.Invocations).Count().IsEqualTo(1); // only the new call - await Assert.That(loggerMock.Invocations).Count().IsEqualTo(0); // history cleared + await Assert.That(Mock.GetInvocations(serviceMock)).Count().IsEqualTo(1); // only the new call + await Assert.That(Mock.GetInvocations(loggerMock)).Count().IsEqualTo(0); // history cleared } [Test] @@ -149,7 +149,7 @@ public async Task Repository_Uses_Default_Behavior() var mock = repo.Of(); // Assert — mock inherits strict behavior - await Assert.That(mock.Behavior).IsEqualTo(MockBehavior.Strict); + await Assert.That(Mock.GetBehavior(mock)).IsEqualTo(MockBehavior.Strict); } [Test] @@ -160,7 +160,7 @@ public async Task Repository_Behavior_Can_Be_Overridden_Per_Mock() var looseMock = repo.Of(MockBehavior.Loose); // Assert — specific behavior overrides repository default - await Assert.That(looseMock.Behavior).IsEqualTo(MockBehavior.Loose); + await Assert.That(Mock.GetBehavior(looseMock)).IsEqualTo(MockBehavior.Loose); } [Test] @@ -274,7 +274,7 @@ public async Task Repository_OfPartial_With_Strict_Behavior() var mock = repo.OfPartial(); // Assert — partial mock inherits strict behavior from repository - await Assert.That(mock.Behavior).IsEqualTo(MockBehavior.Strict); + await Assert.That(Mock.GetBehavior(mock)).IsEqualTo(MockBehavior.Strict); } [Test] @@ -285,6 +285,6 @@ public async Task Repository_OfPartial_Behavior_Override() var mock = repo.OfPartial(MockBehavior.Loose); // Assert — behavior overridden - await Assert.That(mock.Behavior).IsEqualTo(MockBehavior.Loose); + await Assert.That(Mock.GetBehavior(mock)).IsEqualTo(MockBehavior.Loose); } } diff --git a/TUnit.Mocks.Tests/MultipleInterfaceTests.cs b/TUnit.Mocks.Tests/MultipleInterfaceTests.cs index 06894335c5..3cadd68826 100644 --- a/TUnit.Mocks.Tests/MultipleInterfaceTests.cs +++ b/TUnit.Mocks.Tests/MultipleInterfaceTests.cs @@ -114,7 +114,7 @@ public async Task Multi_Mock_Shares_Single_Engine() ((IMultiDisposable)mock.Object).Dispose(); // Assert — all calls recorded in invocations - await Assert.That(mock.Invocations).Count().IsEqualTo(2); + await Assert.That(Mock.GetInvocations(mock)).Count().IsEqualTo(2); } [Test] @@ -180,7 +180,7 @@ public async Task Mock_Of_Four_Interfaces_Tracks_All_Calls() ((IMultiCloneable)mock.Object).Clone(); // Assert — all 4 calls tracked - await Assert.That(mock.Invocations).Count().IsEqualTo(4); + await Assert.That(Mock.GetInvocations(mock)).Count().IsEqualTo(4); } [Test] @@ -190,6 +190,6 @@ public async Task Mock_Of_Two_Interfaces_With_Strict_Behavior() var mock = Mock.Of(MockBehavior.Strict); // Assert — strict behavior inherited - await Assert.That(mock.Behavior).IsEqualTo(MockBehavior.Strict); + await Assert.That(Mock.GetBehavior(mock)).IsEqualTo(MockBehavior.Strict); } } diff --git a/TUnit.Mocks.Tests/OrderedVerificationTests.cs b/TUnit.Mocks.Tests/OrderedVerificationTests.cs index 7db7410248..2fba4d2305 100644 --- a/TUnit.Mocks.Tests/OrderedVerificationTests.cs +++ b/TUnit.Mocks.Tests/OrderedVerificationTests.cs @@ -340,7 +340,7 @@ public void VerifyInOrder_Marks_Calls_As_Verified_For_VerifyNoOtherCalls() }); // This should pass because the calls above were verified in VerifyInOrder - mock.VerifyNoOtherCalls(); + Mock.VerifyNoOtherCalls(mock); } [Test] @@ -365,7 +365,7 @@ public async Task VerifyInOrder_Partial_Verification_Leaves_Unverified_Calls() // Log("hello") was not verified, so this should fail var exception = Assert.Throws(() => { - mock.VerifyNoOtherCalls(); + Mock.VerifyNoOtherCalls(mock); }); await Assert.That(exception.Message).Contains("Log(hello)"); diff --git a/TUnit.Mocks.Tests/ProtectedMemberTests.cs b/TUnit.Mocks.Tests/ProtectedMemberTests.cs index bfa0833fa9..52f6120882 100644 --- a/TUnit.Mocks.Tests/ProtectedMemberTests.cs +++ b/TUnit.Mocks.Tests/ProtectedMemberTests.cs @@ -88,7 +88,7 @@ public async Task Protected_Method_Calls_Are_Recorded_In_Invocations() mock.Object.ProcessAndFormat(7); // Assert — both ComputeValue (base call) and FormatResult are recorded - await Assert.That(mock.Invocations).Count().IsGreaterThanOrEqualTo(2); + await Assert.That(Mock.GetInvocations(mock)).Count().IsGreaterThanOrEqualTo(2); } [Test] diff --git a/TUnit.Mocks.Tests/ResetTests.cs b/TUnit.Mocks.Tests/ResetTests.cs index 008c5f2e90..470c065404 100644 --- a/TUnit.Mocks.Tests/ResetTests.cs +++ b/TUnit.Mocks.Tests/ResetTests.cs @@ -23,7 +23,7 @@ public async Task Reset_Clears_All_Setups() await Assert.That(calc.Add(3, 4)).IsEqualTo(99); // Act - mock.Reset(); + Mock.Reset(mock); // Assert — after reset, all setups are gone, returns default await Assert.That(calc.Add(1, 2)).IsEqualTo(0); @@ -44,7 +44,7 @@ public async Task Reset_Clears_Call_History() mock.Add(Arg.Any(), Arg.Any()).WasCalled(Times.Exactly(3)); // Act - mock.Reset(); + Mock.Reset(mock); // Assert — after reset, call history is cleared mock.Add(Arg.Any(), Arg.Any()).WasNeverCalled(); @@ -62,7 +62,7 @@ public async Task Reset_Allows_Fresh_Setup() await Assert.That(calc.Add(1, 2)).IsEqualTo(42); // Act — reset and reconfigure with different return value - mock.Reset(); + Mock.Reset(mock); mock.Add(1, 2).Returns(100); // Assert — new setup is in effect @@ -81,7 +81,7 @@ public async Task Reset_Allows_Fresh_Verification() mock.Add(1, 2).WasCalled(Times.Exactly(2)); // Act - mock.Reset(); + Mock.Reset(mock); // Make one new call calc.Add(1, 2); @@ -102,7 +102,7 @@ public async Task Reset_On_Strict_Mock_Restores_Strict_Behavior() await Assert.That(calc.Add(1, 2)).IsEqualTo(3); // Act — reset clears all setups - mock.Reset(); + Mock.Reset(mock); // Assert — strict mock throws again for unconfigured calls var exception = Assert.Throws(() => @@ -125,7 +125,7 @@ public async Task Reset_Clears_String_Method_Setup() await Assert.That(greeter.Greet("Alice")).IsEqualTo("Hello, Alice!"); // Act - mock.Reset(); + Mock.Reset(mock); // Assert — after reset, returns default (empty string for non-nullable string) var result = greeter.Greet("Alice"); @@ -144,7 +144,7 @@ public async Task Reset_Clears_Void_Method_Call_History() mock.Log(Arg.Any()).WasCalled(Times.Exactly(2)); // Act - mock.Reset(); + Mock.Reset(mock); // Assert — void method call history is cleared mock.Log(Arg.Any()).WasNeverCalled(); @@ -163,7 +163,7 @@ public async Task Reset_Followed_By_New_Setup_And_Verification() mock.Add(1, 1).WasCalled(Times.Once); // Act — reset - mock.Reset(); + Mock.Reset(mock); // Re-setup with new values mock.Add(1, 1).Returns(20); @@ -188,17 +188,17 @@ public async Task Multiple_Resets() // First cycle mock.Add(1, 1).Returns(10); await Assert.That(calc.Add(1, 1)).IsEqualTo(10); - mock.Reset(); + Mock.Reset(mock); // Second cycle mock.Add(1, 1).Returns(20); await Assert.That(calc.Add(1, 1)).IsEqualTo(20); - mock.Reset(); + Mock.Reset(mock); // Third cycle mock.Add(1, 1).Returns(30); await Assert.That(calc.Add(1, 1)).IsEqualTo(30); - mock.Reset(); + Mock.Reset(mock); // After final reset — returns default await Assert.That(calc.Add(1, 1)).IsEqualTo(0); diff --git a/TUnit.Mocks.Tests/StateMachineTests.cs b/TUnit.Mocks.Tests/StateMachineTests.cs index 8fe1d67182..28efdf386f 100644 --- a/TUnit.Mocks.Tests/StateMachineTests.cs +++ b/TUnit.Mocks.Tests/StateMachineTests.cs @@ -15,14 +15,14 @@ public class StateMachineTests public async Task State_Machine_Returns_Different_Values_Per_State() { var mock = Mock.Of(); - mock.SetState("disconnected"); + Mock.SetState(mock, "disconnected"); - mock.InState("disconnected", m => + Mock.InState(mock, "disconnected", m => { m.GetStatus().Returns("OFFLINE"); }); - mock.InState("connected", m => + Mock.InState(mock, "connected", m => { m.GetStatus().Returns("ONLINE"); }); @@ -31,10 +31,10 @@ public async Task State_Machine_Returns_Different_Values_Per_State() await Assert.That(conn.GetStatus()).IsEqualTo("OFFLINE"); - mock.SetState("connected"); + Mock.SetState(mock, "connected"); await Assert.That(conn.GetStatus()).IsEqualTo("ONLINE"); - mock.SetState("disconnected"); + Mock.SetState(mock, "disconnected"); await Assert.That(conn.GetStatus()).IsEqualTo("OFFLINE"); } @@ -42,15 +42,15 @@ public async Task State_Machine_Returns_Different_Values_Per_State() public async Task TransitionsTo_Changes_State_After_Call() { var mock = Mock.Of(); - mock.SetState("disconnected"); + Mock.SetState(mock, "disconnected"); - mock.InState("disconnected", m => + Mock.InState(mock, "disconnected", m => { m.Connect().TransitionsTo("connected"); m.GetStatus().Returns("OFFLINE"); }); - mock.InState("connected", m => + Mock.InState(mock, "connected", m => { m.Disconnect().TransitionsTo("disconnected"); m.GetStatus().Returns("ONLINE"); @@ -71,14 +71,14 @@ public async Task TransitionsTo_Changes_State_After_Call() public async Task State_Scoped_Throws() { var mock = Mock.Of(); - mock.SetState("connected"); + Mock.SetState(mock, "connected"); - mock.InState("connected", m => + Mock.InState(mock, "connected", m => { m.Connect().Throws(); }); - mock.InState("disconnected", m => + Mock.InState(mock, "disconnected", m => { m.Disconnect().Throws(); }); @@ -97,7 +97,7 @@ public async Task State_Scoped_Throws() public async Task No_State_Setups_Match_In_Any_State() { var mock = Mock.Of(); - mock.SetState("disconnected"); + Mock.SetState(mock, "disconnected"); // Setup without state guard — matches in any state mock.GetStatus().Returns("ALWAYS"); @@ -105,10 +105,10 @@ public async Task No_State_Setups_Match_In_Any_State() IConnection conn = mock.Object; await Assert.That(conn.GetStatus()).IsEqualTo("ALWAYS"); - mock.SetState("connected"); + Mock.SetState(mock, "connected"); await Assert.That(conn.GetStatus()).IsEqualTo("ALWAYS"); - mock.SetState(null); + Mock.SetState(mock, null); await Assert.That(conn.GetStatus()).IsEqualTo("ALWAYS"); } @@ -121,7 +121,7 @@ public async Task State_Scoped_Setup_Overrides_Global_When_In_State() mock.GetStatus().Returns("DEFAULT"); // State-scoped setup - mock.InState("special", m => + Mock.InState(mock, "special", m => { m.GetStatus().Returns("SPECIAL"); }); @@ -132,11 +132,11 @@ public async Task State_Scoped_Setup_Overrides_Global_When_In_State() await Assert.That(conn.GetStatus()).IsEqualTo("DEFAULT"); // State set — scoped setup wins (last-wins semantics, state-scoped was added later) - mock.SetState("special"); + Mock.SetState(mock, "special"); await Assert.That(conn.GetStatus()).IsEqualTo("SPECIAL"); // Different state — scoped doesn't match, global wins - mock.SetState("other"); + Mock.SetState(mock, "other"); await Assert.That(conn.GetStatus()).IsEqualTo("DEFAULT"); } @@ -144,9 +144,9 @@ public async Task State_Scoped_Setup_Overrides_Global_When_In_State() public async Task Strict_Mode_Throws_For_Unconfigured_Call_In_State() { var mock = Mock.Of(MockBehavior.Strict); - mock.SetState("disconnected"); + Mock.SetState(mock, "disconnected"); - mock.InState("disconnected", m => + Mock.InState(mock, "disconnected", m => { m.GetStatus().Returns("OFFLINE"); }); @@ -163,14 +163,14 @@ public async Task Nested_InState_Restores_Previous_State_Scope() { // Regression test: nested InState calls must save/restore PendingRequiredState var mock = Mock.Of(); - mock.SetState("outer"); + Mock.SetState(mock, "outer"); - mock.InState("outer", m => + Mock.InState(mock, "outer", m => { m.GetStatus().Returns("OUTER"); // Nested InState should temporarily switch to "inner" scope - mock.InState("inner", m => + Mock.InState(mock, "inner", m => { m.Connect(); }); @@ -186,7 +186,7 @@ public async Task Nested_InState_Restores_Previous_State_Scope() conn.Disconnect(); // should not throw (setup registered in outer scope) // In "inner" state, Connect should work (it was set up in inner scope) - mock.SetState("inner"); + Mock.SetState(mock, "inner"); conn.Connect(); // should not throw } @@ -199,18 +199,18 @@ public async Task SetState_Null_Clears_State() mock.GetStatus().Returns("NO_STATE"); // State-scoped setup — added second, wins when in "connected" state - mock.InState("connected", m => + Mock.InState(mock, "connected", m => { m.GetStatus().Returns("ONLINE"); }); - mock.SetState("connected"); + Mock.SetState(mock, "connected"); IConnection conn = mock.Object; await Assert.That(conn.GetStatus()).IsEqualTo("ONLINE"); // Clear state — scoped setup no longer matches, global setup wins - mock.SetState(null); + Mock.SetState(mock, null); await Assert.That(conn.GetStatus()).IsEqualTo("NO_STATE"); } @@ -218,21 +218,21 @@ public async Task SetState_Null_Clears_State() public async Task Reset_Clears_State() { var mock = Mock.Of(); - mock.SetState("connected"); + Mock.SetState(mock, "connected"); - mock.Reset(); + Mock.Reset(mock); - // After reset, Engine.CurrentState should be null - await Assert.That(mock.Engine.CurrentState).IsNull(); + // After reset, current state should be null + await Assert.That(Mock.GetEngine(mock).CurrentState).IsNull(); } [Test] public async Task Verify_Works_With_State_Scoped_Setups() { var mock = Mock.Of(); - mock.SetState("disconnected"); + Mock.SetState(mock, "disconnected"); - mock.InState("disconnected", m => + Mock.InState(mock, "disconnected", m => { m.Connect().TransitionsTo("connected"); }); diff --git a/TUnit.Mocks.Tests/StrictModeTests.cs b/TUnit.Mocks.Tests/StrictModeTests.cs index 336674622a..e980c9461a 100644 --- a/TUnit.Mocks.Tests/StrictModeTests.cs +++ b/TUnit.Mocks.Tests/StrictModeTests.cs @@ -65,7 +65,7 @@ public async Task Strict_Configured_Void_Method_Does_Not_Throw() // Act & Assert — configured void method should not throw ICalculator calc = mock.Object; calc.Log("expected message"); - await Assert.That(mock.Behavior).IsEqualTo(MockBehavior.Strict); + await Assert.That(Mock.GetBehavior(mock)).IsEqualTo(MockBehavior.Strict); } [Test] @@ -197,6 +197,6 @@ public async Task Loose_Is_Default_Behavior() // Assert await Assert.That(result).IsEqualTo(0); - await Assert.That(mock.Behavior).IsEqualTo(MockBehavior.Loose); + await Assert.That(Mock.GetBehavior(mock)).IsEqualTo(MockBehavior.Loose); } } diff --git a/TUnit.Mocks.Tests/VerifyAllTests.cs b/TUnit.Mocks.Tests/VerifyAllTests.cs index 791f72dba3..6d437ee99b 100644 --- a/TUnit.Mocks.Tests/VerifyAllTests.cs +++ b/TUnit.Mocks.Tests/VerifyAllTests.cs @@ -21,7 +21,7 @@ public async Task VerifyAll_Passes_When_All_Setups_Invoked() svc.GetValue("key"); svc.Process(1); - mock.VerifyAll(); + Mock.VerifyAll(mock); await Assert.That(true).IsTrue(); } @@ -35,7 +35,7 @@ public async Task VerifyAll_Fails_When_Setup_Not_Invoked() var svc = mock.Object; svc.GetValue("key"); // Only call GetValue, not Process - var ex = Assert.Throws(() => mock.VerifyAll()); + var ex = Assert.Throws(() => Mock.VerifyAll(mock)); await Assert.That(ex.Message).Contains("Process"); } @@ -45,7 +45,7 @@ public async Task VerifyAll_Fails_When_No_Setups_Called() var mock = Mock.Of(); mock.GetValue(Arg.Any()).Returns("value"); - var ex = Assert.Throws(() => mock.VerifyAll()); + var ex = Assert.Throws(() => Mock.VerifyAll(mock)); await Assert.That(ex.Message).Contains("GetValue"); } @@ -53,7 +53,7 @@ public async Task VerifyAll_Fails_When_No_Setups_Called() public async Task VerifyAll_Passes_When_No_Setups_Registered() { var mock = Mock.Of(); - mock.VerifyAll(); // No setups = nothing to verify + Mock.VerifyAll(mock); // No setups = nothing to verify await Assert.That(true).IsTrue(); } @@ -65,7 +65,7 @@ public async Task VerifyAll_Multiple_Uninvoked_Shows_All() mock.Process(42); // Don't call anything - var ex = Assert.Throws(() => mock.VerifyAll()); + var ex = Assert.Throws(() => Mock.VerifyAll(mock)); await Assert.That(ex.Message).Contains("GetValue"); await Assert.That(ex.Message).Contains("Process"); } diff --git a/TUnit.Mocks.Tests/VerifyNoOtherCallsTests.cs b/TUnit.Mocks.Tests/VerifyNoOtherCallsTests.cs index e061933606..8ec10cb614 100644 --- a/TUnit.Mocks.Tests/VerifyNoOtherCallsTests.cs +++ b/TUnit.Mocks.Tests/VerifyNoOtherCallsTests.cs @@ -21,7 +21,7 @@ public async Task VerifyNoOtherCalls_Passes_When_All_Calls_Verified() svc.GetValue("key1"); mock.GetValue("key1").WasCalled(Times.Once); - mock.VerifyNoOtherCalls(); + Mock.VerifyNoOtherCalls(mock); await Assert.That(true).IsTrue(); } @@ -39,7 +39,7 @@ public async Task VerifyNoOtherCalls_Fails_When_Unverified_Calls_Exist() // Only verify GetValue, not Process mock.GetValue("key1").WasCalled(Times.Once); - var ex = Assert.Throws(() => mock.VerifyNoOtherCalls()); + var ex = Assert.Throws(() => Mock.VerifyNoOtherCalls(mock)); await Assert.That(ex.Message).Contains("Process(42)"); } @@ -47,7 +47,7 @@ public async Task VerifyNoOtherCalls_Fails_When_Unverified_Calls_Exist() public async Task VerifyNoOtherCalls_Passes_When_No_Calls_Made() { var mock = Mock.Of(); - mock.VerifyNoOtherCalls(); + Mock.VerifyNoOtherCalls(mock); await Assert.That(true).IsTrue(); } @@ -60,8 +60,8 @@ public async Task VerifyNoOtherCalls_Works_After_Reset() var svc = mock.Object; svc.GetValue("key1"); - mock.Reset(); - mock.VerifyNoOtherCalls(); // should pass — history cleared + Mock.Reset(mock); + Mock.VerifyNoOtherCalls(mock); // should pass — history cleared await Assert.That(true).IsTrue(); } @@ -78,7 +78,7 @@ public async Task VerifyNoOtherCalls_Multiple_Unverified_Shows_All() svc.Reset(); // Verify none - var ex = Assert.Throws(() => mock.VerifyNoOtherCalls()); + var ex = Assert.Throws(() => Mock.VerifyNoOtherCalls(mock)); await Assert.That(ex.Message).Contains("GetValue(a)"); await Assert.That(ex.Message).Contains("Process(1)"); } diff --git a/TUnit.Mocks/IMockEngineAccessOfT.cs b/TUnit.Mocks/IMockEngineAccessOfT.cs new file mode 100644 index 0000000000..20acf053f4 --- /dev/null +++ b/TUnit.Mocks/IMockEngineAccessOfT.cs @@ -0,0 +1,13 @@ +using System.ComponentModel; + +namespace TUnit.Mocks; + +/// +/// Provides access to the mock engine for generated code. Not intended for direct use. +/// +[EditorBrowsable(EditorBrowsableState.Never)] +public interface IMockEngineAccess where T : class +{ + /// The mock engine instance. + MockEngine Engine { get; } +} diff --git a/TUnit.Mocks/Mock.cs b/TUnit.Mocks/Mock.cs index 2a8b23c6ce..2013837af9 100644 --- a/TUnit.Mocks/Mock.cs +++ b/TUnit.Mocks/Mock.cs @@ -1,4 +1,6 @@ using System.Collections.Concurrent; +using System.Runtime.CompilerServices; +using TUnit.Mocks.Diagnostics; using TUnit.Mocks.Verification; namespace TUnit.Mocks; @@ -8,6 +10,10 @@ namespace TUnit.Mocks; /// public static class Mock { + // Maps mock implementation objects back to their Mock wrappers. + // ConditionalWeakTable so mocks can be GC'd normally. + private static readonly ConditionalWeakTable _objectToMock = new(); + // The source generator registers factories via this method at module initialization time. // ConcurrentDictionary is used because module initializers from multiple assemblies // can run concurrently when test assemblies are loaded in parallel. @@ -25,6 +31,49 @@ public static class Mock // Separate registry for wrap mock factories that accept a real instance. private static readonly ConcurrentDictionary> _wrapFactories = new(); + /// + /// Registers the mapping from a mock implementation object to its wrapper. + /// Called from the constructor. Not intended for direct use. + /// + internal static void Register(object mockObject, IMock mockWrapper) + { +#if NET7_0_OR_GREATER + _objectToMock.AddOrUpdate(mockObject, mockWrapper); +#else + // ConditionalWeakTable.AddOrUpdate not available before .NET 7. + // Each mock object is unique (created by new), so Add will not throw. + _objectToMock.Add(mockObject, mockWrapper); +#endif + } + + /// + /// Retrieves the wrapper for a mock implementation object. + /// Use this to access the mock wrapper from auto-mocked return values or any mocked object. + /// + /// + /// + /// var mock = Mock.Of<IServiceA>(); + /// var serviceB = mock.Object.GetServiceB(); // auto-mocked + /// var autoMock = Mock.Get(serviceB); // get the wrapper + /// autoMock.GetValue().Returns(42); + /// + /// + public static Mock Get(T mockedObject) where T : class + { + if (_objectToMock.TryGetValue(mockedObject, out var mock)) + { + if (mock is Mock typed) + return typed; + + throw new InvalidOperationException( + $"The object is a mock of '{mock.GetType().GenericTypeArguments[0].Name}', not '{typeof(T).Name}'."); + } + + throw new InvalidOperationException( + $"The object of type '{typeof(T).Name}' is not a mock. " + + $"Mock.Get can only be used with objects created by Mock.Of, auto-mocking, or other Mock factory methods."); + } + /// /// Registers a factory for creating mocks of type T. Called by generated code. /// Not intended for direct use. @@ -82,7 +131,7 @@ public static void RegisterWrapFactory(Func> factory public static Mock Of(MockBehavior behavior, IDefaultValueProvider defaultValueProvider) where T : class { var mock = Of(behavior); - mock.DefaultValueProvider = defaultValueProvider; + GetEngine(mock).DefaultValueProvider = defaultValueProvider; return mock; } @@ -251,4 +300,93 @@ public static void VerifyInOrder(Action verificationActions) { OrderedVerification.Verify(verificationActions); } + + // ────────────────────────────────────────────────────────── + // Static helpers – expose control surface without cluttering Mock + // ────────────────────────────────────────────────────────── + + /// + /// Gets the mock engine for generated code. Not intended for direct use. + /// + [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] + public static MockEngine GetEngine(Mock mock) where T : class + => ((IMockEngineAccess)mock).Engine; + + /// All calls made to this mock, in order. + public static IReadOnlyList GetInvocations(Mock mock) where T : class + => GetEngine(mock).GetAllCalls(); + + /// Returns the mock behavior (Loose or Strict). + public static MockBehavior GetBehavior(Mock mock) where T : class + => GetEngine(mock).Behavior; + + /// + /// Gets the custom default value provider for unconfigured methods in loose mode. + /// When set, this provider is consulted before auto-mocking and built-in defaults. + /// + public static IDefaultValueProvider? GetDefaultValueProvider(Mock mock) where T : class + => GetEngine(mock).DefaultValueProvider; + + /// + /// Sets the custom default value provider for unconfigured methods in loose mode. + /// When set, this provider is consulted before auto-mocking and built-in defaults. + /// + public static void SetDefaultValueProvider(Mock mock, IDefaultValueProvider? provider) where T : class + => GetEngine(mock).DefaultValueProvider = provider; + + /// + /// Enables auto-tracking for all properties. Property setters store values and getters return them, + /// acting like real auto-properties. Explicit setups take precedence over auto-tracked values. + /// + public static void SetupAllProperties(Mock mock) where T : class + => GetEngine(mock).AutoTrackProperties = true; + + /// Clears all setups and call history. + public static void Reset(Mock mock) where T : class + => GetEngine(mock).Reset(); + + /// + /// Verifies all registered setups were invoked at least once. + /// Throws listing uninvoked setups. + /// + public static void VerifyAll(Mock mock) where T : class + => ((IMock)mock).VerifyAll(); + + /// + /// Fails if any recorded call was not matched by a prior verification statement. + /// Throws listing unverified calls. + /// + public static void VerifyNoOtherCalls(Mock mock) where T : class + => ((IMock)mock).VerifyNoOtherCalls(); + + /// + /// Returns a diagnostic report of this mock's setup coverage and call matching. + /// + public static MockDiagnostics GetDiagnostics(Mock mock) where T : class + => GetEngine(mock).GetDiagnostics(); + + /// + /// Sets the current state for state machine mocking. Null clears the state. + /// + public static void SetState(Mock mock, string? stateName) where T : class + => GetEngine(mock).TransitionTo(stateName); + + /// + /// Configures setups scoped to a specific state. All setups registered inside + /// the action will only match when the engine is in the specified state. + /// + public static void InState(Mock mock, string stateName, Action> configure) where T : class + { + var engine = GetEngine(mock); + var previous = engine.PendingRequiredState; + engine.PendingRequiredState = stateName; + try + { + configure(mock); + } + finally + { + engine.PendingRequiredState = previous; + } + } } diff --git a/TUnit.Mocks/MockOfT.cs b/TUnit.Mocks/MockOfT.cs index e4ef159ffd..4f9550ca11 100644 --- a/TUnit.Mocks/MockOfT.cs +++ b/TUnit.Mocks/MockOfT.cs @@ -8,11 +8,9 @@ namespace TUnit.Mocks; /// Wraps a generated mock implementation. Source-generated extension methods on Mock<T> /// provide strongly-typed setup and verification members directly. /// -public class Mock : IMock where T : class +public class Mock : IMock, IMockEngineAccess where T : class { - /// The mock engine. Used by generated code. Not intended for direct use. - [EditorBrowsable(EditorBrowsableState.Never)] - public MockEngine Engine { get; } + private readonly MockEngine _engine; /// The mock object that implements T. public T Object { get; } @@ -20,49 +18,22 @@ public class Mock : IMock where T : class /// object IMock.ObjectInstance => Object; - /// The mock behavior (Loose or Strict). - public MockBehavior Behavior => Engine.Behavior; - - /// - /// Gets or sets the custom default value provider for unconfigured methods in loose mode. - /// When set, this provider is consulted before auto-mocking and built-in defaults. - /// - public IDefaultValueProvider? DefaultValueProvider - { - get => Engine.DefaultValueProvider; - set => Engine.DefaultValueProvider = value; - } + /// + MockEngine IMockEngineAccess.Engine => _engine; /// Creates a Mock wrapping the given object and engine. Used by generated code. [EditorBrowsable(EditorBrowsableState.Never)] public Mock(T mockObject, MockEngine engine) { - Engine = engine; + _engine = engine; Object = mockObject; + Mock.Register(mockObject, this); } - /// All calls made to this mock, in order. - public IReadOnlyList Invocations => Engine.GetAllCalls(); - - /// - /// Enables auto-tracking for all properties. Property setters store values and getters return them, - /// acting like real auto-properties. Explicit setups take precedence over auto-tracked values. - /// - public void SetupAllProperties() - { - Engine.AutoTrackProperties = true; - } - - /// Clears all setups and call history. - public void Reset() => Engine.Reset(); - - /// - /// Verifies all registered setups were invoked at least once. - /// Throws listing uninvoked setups. - /// - public void VerifyAll() + /// + void IMock.VerifyAll() { - var setups = Engine.GetSetups(); + var setups = _engine.GetSetups(); var uninvoked = new List(); foreach (var setup in setups) { @@ -84,13 +55,10 @@ public void VerifyAll() } } - /// - /// Fails if any recorded call was not matched by a prior verification statement. - /// Throws listing unverified calls. - /// - public void VerifyNoOtherCalls() + /// + void IMock.VerifyNoOtherCalls() { - var unverified = Engine.GetUnverifiedCalls(); + var unverified = _engine.GetUnverifiedCalls(); if (unverified.Count > 0) { var message = $"VerifyNoOtherCalls failed. The following calls were not verified:\n" + @@ -99,50 +67,8 @@ public void VerifyNoOtherCalls() } } - /// - /// Retrieves the auto-mock wrapper for a child interface returned by this mock. - /// Use this to configure auto-mocked return values. - /// - public Mock GetAutoMock(string memberName) where TChild : class - { - var cacheKey = memberName + "|" + typeof(TChild).FullName; - if (Engine.TryGetAutoMock(cacheKey, out var mock)) - { - return (Mock)mock; - } - - throw new InvalidOperationException( - $"No auto-mock found for member '{memberName}' returning type '{typeof(TChild).Name}'. " + - $"Ensure the method was called at least once before retrieving its auto-mock."); - } - - /// - /// Returns a diagnostic report of this mock's setup coverage and call matching. - /// - public Diagnostics.MockDiagnostics GetDiagnostics() => Engine.GetDiagnostics(); - - /// - /// Sets the current state for state machine mocking. Null clears the state. - /// - public void SetState(string? stateName) => Engine.TransitionTo(stateName); - - /// - /// Configures setups scoped to a specific state. All setups registered inside - /// the action will only match when the engine is in the specified state. - /// - public void InState(string stateName, Action> configure) - { - var previous = Engine.PendingRequiredState; - Engine.PendingRequiredState = stateName; - try - { - configure(this); - } - finally - { - Engine.PendingRequiredState = previous; - } - } + /// + void IMock.Reset() => _engine.Reset(); /// Implicit conversion to T -- no .Object needed. public static implicit operator T(Mock mock) => mock.Object; diff --git a/docs/docs/test-authoring/mocking/advanced.md b/docs/docs/test-authoring/mocking/advanced.md index e5b4a71f76..61b07785e7 100644 --- a/docs/docs/test-authoring/mocking/advanced.md +++ b/docs/docs/test-authoring/mocking/advanced.md @@ -74,15 +74,15 @@ public interface IConnection } var mock = Mock.Of(); -mock.SetState("disconnected"); +Mock.SetState(mock, "disconnected"); -mock.InState("disconnected", m => +Mock.InState(mock, "disconnected", m => { m.GetStatus().Returns("OFFLINE"); m.Connect().TransitionsTo("connected"); }); -mock.InState("connected", m => +Mock.InState(mock, "connected", m => { m.GetStatus().Returns("ONLINE"); m.Disconnect().TransitionsTo("disconnected"); @@ -102,9 +102,9 @@ status = mock.Object.GetStatus(); // "OFFLINE" | Method | Description | |---|---| -| `mock.SetState("name")` | Set the current state | -| `mock.SetState(null)` | Clear state (all setups match) | -| `mock.InState("name", configure)` | Register setups scoped to a state | +| `Mock.SetState(mock, "name")` | Set the current state | +| `Mock.SetState(mock, null)` | Clear state (all setups match) | +| `Mock.InState(mock, "name", configure)` | Register setups scoped to a state | | `.TransitionsTo("name")` | Transition state after method call (on setup chain) | ## Recursive / Auto-Mocking @@ -128,14 +128,14 @@ var mock = Mock.Of(); var serviceB = mock.Object.GetServiceB(); // serviceB is not null — it's a working mock -// Configure the auto-mock -var autoMock = mock.GetAutoMock("GetServiceB"); +// Configure the auto-mock via Mock.Get +var autoMock = Mock.Get(serviceB); autoMock.GetValue().Returns(42); var value = serviceB.GetValue(); // 42 ``` -Auto-mocks are cached — calling the same method returns the same mock instance. +Use `Mock.Get(obj)` to retrieve the `Mock` wrapper for any mock object — auto-mocked return values, or any object created by `Mock.Of`. Auto-mocks are cached — calling the same method returns the same mock instance. ## MockRepository @@ -187,7 +187,7 @@ mock.Delete(Arg.Any()); svc.GetUser(1); // Delete was never called -var diag = mock.GetDiagnostics(); +var diag = Mock.GetDiagnostics(mock); diag.TotalSetups; // 2 diag.ExercisedSetups; // 1 diag.UnusedSetups; // [Delete(Arg.Any())] @@ -215,7 +215,7 @@ public class TestDefaults : IDefaultValueProvider } var mock = Mock.Of(); -mock.DefaultValueProvider = new TestDefaults(); +Mock.SetDefaultValueProvider(mock, new TestDefaults()); var name = mock.Object.GetName(); // "test-default" (no setup needed) var count = mock.Object.GetCount(); // -1 @@ -231,10 +231,10 @@ Clear all setups, call history, state, and auto-tracked property values: mock.GetUser(Arg.Any()).Returns(new User("Alice")); svc.GetUser(1); -mock.Reset(); +Mock.Reset(mock); svc.GetUser(1); // returns default (setup cleared) -mock.Invocations.Count; // 0 (history cleared) +Mock.GetInvocations(mock).Count; // 0 (history cleared) ``` The `SetupAllProperties()` flag is preserved across resets. diff --git a/docs/docs/test-authoring/mocking/setup.md b/docs/docs/test-authoring/mocking/setup.md index 579651d467..034bb67e11 100644 --- a/docs/docs/test-authoring/mocking/setup.md +++ b/docs/docs/test-authoring/mocking/setup.md @@ -116,7 +116,7 @@ Call `SetupAllProperties()` to make properties behave like real auto-properties ```csharp var mock = Mock.Of(); -mock.SetupAllProperties(); +Mock.SetupAllProperties(mock); mock.Object.Name = "Alice"; var name = mock.Object.Name; // "Alice" diff --git a/docs/docs/test-authoring/mocking/verification.md b/docs/docs/test-authoring/mocking/verification.md index f49041368b..de3ff19de9 100644 --- a/docs/docs/test-authoring/mocking/verification.md +++ b/docs/docs/test-authoring/mocking/verification.md @@ -104,7 +104,7 @@ mock.Delete(Arg.Any()); svc.GetUser(1); svc.Delete(2); -mock.VerifyAll(); // passes — both setups were invoked +Mock.VerifyAll(mock); // passes — both setups were invoked ``` If any setup was never called, `VerifyAll` throws listing the uninvoked setups. @@ -120,7 +120,7 @@ svc.Delete(2); mock.GetUser(1).WasCalled(Times.Once); mock.Delete(2).WasCalled(Times.Once); -mock.VerifyNoOtherCalls(); // passes — all calls accounted for +Mock.VerifyNoOtherCalls(mock); // passes — all calls accounted for ``` If there are unverified calls, `VerifyNoOtherCalls` throws listing them. @@ -146,7 +146,7 @@ This integrates with TUnit's assertion engine — failures appear as assertion e Access the raw call history for custom inspection: ```csharp -var calls = mock.Invocations; +var calls = Mock.GetInvocations(mock); await Assert.That(calls).HasCount().EqualTo(3); await Assert.That(calls[0].MemberName).IsEqualTo("GetUser");