Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
d02862f
feat(mocks): add MockMethodCall<TReturn> and VoidMockMethodCall unifi…
thomhurst Feb 23, 2026
88d5d29
feat(mocks): add PropertyMockCall<TProperty> and PropertySetterMockCa…
thomhurst Feb 23, 2026
9e29594
feat(mocks): simplify Mock<T> by removing Setup/Verify/Raise surfaces
thomhurst Feb 23, 2026
4ac0258
feat(mocks): replace MockSetupBuilder + MockVerifyBuilder with unifie…
thomhurst Feb 23, 2026
05814e9
refactor: update RaiseBuilder and factory builders for simplified Moc…
thomhurst Feb 23, 2026
d0a70f0
refactor(mocks): update all test files for unified Mock<T> API
thomhurst Feb 23, 2026
ff689f0
fix(mocks): eagerly register void method and property setter setups
thomhurst Feb 23, 2026
c41427e
docs: update mocking documentation for unified Mock<T> API
thomhurst Feb 23, 2026
f225a2c
fix(mocks): use Lazy<T> for thread-safe setup registration in non-voi…
thomhurst Feb 23, 2026
f5f06f4
fix(mocks): address review feedback — member names, setter registrati…
thomhurst Feb 23, 2026
78c57a3
fix(mocks): use Lazy<T> in PropertySetterMockCall for consistency
thomhurst Feb 23, 2026
c6690cc
fix(mocks): cache matchers/name in PropertySetterMockCall, clarify ge…
thomhurst Feb 23, 2026
7db470f
feat(mocks): generate strongly-typed SetsOut/SetsRef methods for out/…
thomhurst Feb 23, 2026
9346b08
fix(mocks): address review feedback — attribute ordering, modern syntax
thomhurst Feb 23, 2026
f7495b3
fix(mocks): eliminate PropertySetterMockCall duplication, consolidate…
thomhurst Feb 23, 2026
42b8448
fix(mocks): document eager registration rationale on PropertySetterMo…
thomhurst Feb 23, 2026
b603826
fix(mocks): use Lazy<T> in generated void wrapper classes for thread …
thomhurst Feb 23, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
refactor: update RaiseBuilder and factory builders for simplified Moc…
…k<T> constructor

- MockRaiseBuilder: Remove _MockRaise holder class and IMockRaise<T> marker
  interface. Extension methods now target Mock<T> directly with Raise prefix
  (e.g., RaiseOnMessage). Event raising goes through IRaisable interface.
- MockFactoryBuilder: Remove creation of setup/verify/raise intermediary
  objects from all three factory methods. Use simplified Mock<T>(impl, engine)
  constructor.
- MockDelegateFactoryBuilder: Same simplification - remove setup/verify
  creation, use Mock<T>(del, engine) constructor.
  • Loading branch information
thomhurst committed Feb 23, 2026
commit 05814e9c12a901b96dccd62f98e9e81745011291
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,7 @@ public static string Build(MockTypeModel model)
writer.AppendLine("};");
}

writer.AppendLine($"var setup = new {safeName}_MockSetup(engine);");
writer.AppendLine($"var verify = new {safeName}_MockVerify(engine);");
writer.AppendLine($"var mock = new global::TUnit.Mocks.Mock<{model.FullyQualifiedName}>(del, setup, verify, engine);");
writer.AppendLine($"var mock = new global::TUnit.Mocks.Mock<{model.FullyQualifiedName}>(del, engine);");
writer.AppendLine("return mock;");
}
}
Expand Down
45 changes: 3 additions & 42 deletions TUnit.Mocks.SourceGenerator/Builders/MockFactoryBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,27 +61,10 @@ private static void BuildInterfaceFactory(CodeWriter writer, MockTypeModel model

using (writer.Block($"private static global::TUnit.Mocks.Mock<{model.FullyQualifiedName}> Create(global::TUnit.Mocks.MockBehavior behavior)"))
{
// For multi-interface mocks: impl uses composite name, setup/verify use primary name
var primarySafeName = MockImplBuilder.GetSafeName(model.FullyQualifiedName);

writer.AppendLine($"var engine = new global::TUnit.Mocks.MockEngine<{model.FullyQualifiedName}>(behavior);");
writer.AppendLine($"var impl = new {safeName}_MockImpl(engine);");
writer.AppendLine("engine.Raisable = impl;");
writer.AppendLine($"var setup = new {primarySafeName}_MockSetup(engine);");
writer.AppendLine($"var verify = new {primarySafeName}_MockVerify(engine);");

// Events: check primary type events (from the single-type model)
// For multi-interface, we only use primary type's raise surface
if (model.Events.Length > 0 && model.AdditionalInterfaceNames.Length == 0)
{
writer.AppendLine($"var raise = new {primarySafeName}_MockRaise(impl);");
writer.AppendLine($"var mock = new global::TUnit.Mocks.Mock<{model.FullyQualifiedName}>(impl, setup, verify, raise, engine);");
}
else
{
writer.AppendLine($"var mock = new global::TUnit.Mocks.Mock<{model.FullyQualifiedName}>(impl, setup, verify, engine);");
}

writer.AppendLine($"var mock = new global::TUnit.Mocks.Mock<{model.FullyQualifiedName}>(impl, engine);");
writer.AppendLine("return mock;");
}
}
Expand All @@ -104,18 +87,7 @@ private static void BuildWrapFactory(CodeWriter writer, MockTypeModel model, str
writer.AppendLine("engine.IsWrapMock = true;");
writer.AppendLine($"var impl = new {safeName}_WrapMockImpl(engine, instance);");
writer.AppendLine("engine.Raisable = impl;");
writer.AppendLine($"var setup = new {safeName}_MockSetup(engine);");
writer.AppendLine($"var verify = new {safeName}_MockVerify(engine);");
if (model.Events.Length > 0)
{
writer.AppendLine($"var raise = new {safeName}_MockRaise(impl);");
writer.AppendLine($"var mock = new global::TUnit.Mocks.Mock<{model.FullyQualifiedName}>(impl, setup, verify, raise, engine);");
}
else
{
writer.AppendLine($"var mock = new global::TUnit.Mocks.Mock<{model.FullyQualifiedName}>(impl, setup, verify, engine);");
}

writer.AppendLine($"var mock = new global::TUnit.Mocks.Mock<{model.FullyQualifiedName}>(impl, engine);");
writer.AppendLine("return mock;");
}
}
Expand All @@ -140,18 +112,7 @@ private static void BuildPartialFactory(CodeWriter writer, MockTypeModel model,
GenerateConstructorDispatch(writer, model, safeName);

writer.AppendLine("engine.Raisable = impl;");
writer.AppendLine($"var setup = new {safeName}_MockSetup(engine);");
writer.AppendLine($"var verify = new {safeName}_MockVerify(engine);");
if (model.Events.Length > 0)
{
writer.AppendLine($"var raise = new {safeName}_MockRaise(impl);");
writer.AppendLine($"var mock = new global::TUnit.Mocks.Mock<{model.FullyQualifiedName}>(impl, setup, verify, raise, engine);");
}
else
{
writer.AppendLine($"var mock = new global::TUnit.Mocks.Mock<{model.FullyQualifiedName}>(impl, setup, verify, engine);");
}

writer.AppendLine($"var mock = new global::TUnit.Mocks.Mock<{model.FullyQualifiedName}>(impl, engine);");
writer.AppendLine("return mock;");
}
}
Expand Down
44 changes: 22 additions & 22 deletions TUnit.Mocks.SourceGenerator/Builders/MockRaiseBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,43 +7,33 @@ internal static class MockRaiseBuilder
public static string Build(MockTypeModel model)
{
var writer = new CodeWriter();
var safeName = MockImplBuilder.GetSafeName(model.FullyQualifiedName);

writer.AppendLine("// <auto-generated/>");
writer.AppendLine("#nullable enable");
writer.AppendLine();

using (writer.Block("namespace TUnit.Mocks.Generated"))
{
// Data holder class implementing marker interface
using (writer.Block($"public sealed class {safeName}_MockRaise : global::TUnit.Mocks.IMockRaise<{model.FullyQualifiedName}>"))
{
writer.AppendLine($"internal readonly {safeName}_MockImpl Impl;");
writer.AppendLine();
writer.AppendLine($"internal {safeName}_MockRaise({safeName}_MockImpl impl) => Impl = impl;");
}

writer.AppendLine();

// Extension methods class
// Extension methods class targeting Mock<T> directly
var safeName = MockImplBuilder.GetSafeName(model.FullyQualifiedName);
using (writer.Block($"public static class {safeName}_MockRaiseExtensions"))
{
bool firstMember = true;
foreach (var evt in model.Events)
{
if (!firstMember) writer.AppendLine();
firstMember = false;
GenerateRaiseMethod(writer, evt, model, safeName);
GenerateRaiseMethod(writer, evt, model);
}
}
}

return writer.ToString();
}

private static void GenerateRaiseMethod(CodeWriter writer, MockEventModel evt, MockTypeModel model, string safeName)
private static void GenerateRaiseMethod(CodeWriter writer, MockEventModel evt, MockTypeModel model)
{
var extensionParam = $"this global::TUnit.Mocks.IMockRaise<{model.FullyQualifiedName}> raise";
var extensionParam = $"this global::TUnit.Mocks.Mock<{model.FullyQualifiedName}> mock";

// Build parameter list: extension param + raise parameters
var raiseParamStr = evt.RaiseParameterList.Length == 0
Expand All @@ -53,15 +43,25 @@ private static void GenerateRaiseMethod(CodeWriter writer, MockEventModel evt, M
? extensionParam
: $"{extensionParam}, {raiseParamStr}";

// Build argument pass-through for the Raise_ call using structured parameter data
var raiseArgNames = evt.RaiseParameterList.Length == 0
? ""
: string.Join(", ", evt.RaiseParameterList.Select(p => p.Name));
// Build args expression for RaiseEvent call
string argsExpr;
if (evt.RaiseParameterList.Length == 0)
{
argsExpr = "null";
}
else if (evt.RaiseParameterList.Length == 1)
{
argsExpr = $"(object?){evt.RaiseParameterList[0].Name}";
}
else
{
var paramNames = string.Join(", ", evt.RaiseParameterList.Select(p => p.Name));
argsExpr = $"(object?)new object?[] {{ {paramNames} }}";
}

using (writer.Block($"public static void {evt.Name}({raiseParams})"))
using (writer.Block($"public static void Raise{evt.Name}({raiseParams})"))
{
writer.AppendLine($"var r = ({safeName}_MockRaise)raise;");
writer.AppendLine($"r.Impl.Raise_{evt.Name}({raiseArgNames});");
writer.AppendLine($"((global::TUnit.Mocks.IRaisable)mock.Engine.Raisable!).RaiseEvent(\"{evt.Name}\", {argsExpr});");
}
}
}