Skip to content

Commit

Permalink
switch to non generic method with type default (#16)
Browse files Browse the repository at this point in the history
* replacing Me<T> with non generic

* update docs and readme

* fix build errors
  • Loading branch information
connorivy authored Dec 4, 2024
1 parent a0d15ee commit c59d818
Show file tree
Hide file tree
Showing 27 changed files with 86 additions and 245 deletions.
1 change: 0 additions & 1 deletion MockMe.sln
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "wiki", "wiki", "{4723EF4B-98A5-4F3D-BEB9-0632A3939D4D}"
ProjectSection(SolutionItems) = preProject
wiki\AdvancedUsage.md = wiki\AdvancedUsage.md
wiki\AvoidingCommonPitfalls.md = wiki\AvoidingCommonPitfalls.md
wiki\HowDoesItWork.md = wiki\HowDoesItWork.md
wiki\QuickStart.md = wiki\QuickStart.md
EndProjectSection
Expand Down
8 changes: 6 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
![MockMeFull](https://github.com/user-attachments/assets/43d8b58f-98b0-4469-95c3-7e5ca0683ffc)

___

[![Coverage Status](https://coveralls.io/repos/github/connorivy/MockMe/badge.svg?branch=main)](https://coveralls.io/github/connorivy/MockMe?branch=main)

## What is it?

MockMe is a library for mocking dependencies in your production code. Unlike other libraries that can only mock interfaces and virtual methods, MockMe can mock sealed classes and non-virtual methods.
Expand All @@ -10,7 +14,7 @@ Download NuGet package, then the source generators and the "MockMe.Mock" type wi

```csharp

var mock = Mock.Me<MyRepo>();
var mock = Mock.Me(default(MyRepo));

mock.Setup.ExpensiveDatabaseCall().Returns(99);

Expand All @@ -21,4 +25,4 @@ mock.Assert.ExpensiveDatabaseCall().WasCalled();

```

Check out the [Wiki](https://github.com/connorivy/MockMe/wiki) for more examples.
Check out the [Wiki](https://github.com/connorivy/MockMe/wiki/QuickStart) for more examples.
4 changes: 2 additions & 2 deletions src/MockMe.Abstractions/GenericMethodDefinitionAttribute.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
using System;
using System.Diagnostics.CodeAnalysis;

namespace MockMe.Abstractions;

[ExcludeFromCodeCoverage]
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
public class GenericMethodDefinitionAttribute(
string typeToReplaceAssemblyName,
Expand All @@ -18,6 +20,4 @@ string sourceTypeMethodName
public string SourceTypeAssemblyName { get; } = sourceTypeAssemblyName;
public string SourceTypeFullName { get; } = sourceTypeFullName;
public string SourceTypeMethodName { get; } = sourceTypeMethodName;

public static string GetCoolMessage() => "This only comes from abstractions";
}
74 changes: 0 additions & 74 deletions src/MockMe.Generator/Extensions/MethodSymbolExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,32 +6,6 @@ namespace MockMe.Generator.Extensions;

public static class MethodSymbolExtensions
{
/// <summary>
///
/// </summary>
/// <param name="method"></param>
/// <returns>
/// A single object type as a string that can hold all items that the method takes as parameters.
/// If a method only takes a string, then this method will return "string",
/// otherwise it will return "ValueTuple of T1, T2, ..., TN"
/// </returns>
public static string GetMethodArgumentsAsCollection(this IMethodSymbol method)
{
var types = method.Parameters.Select(p => p.Type.ToFullTypeString()).ToArray();
if (types.Length == 0)
{
throw new InvalidOperationException(
"Cannot turn list of zero length in argument collection"
);
}
if (types.Length == 1)
{
return types[0];
}

return $"({string.Join(", ", types)})";
}

public static string GetParametersWithOriginalTypesAndModifiers(this IMethodSymbol method) =>
GetParametersWithTypesAndModifiers(method);

Expand Down Expand Up @@ -144,40 +118,6 @@ public static string GetGenericParameterStringInBrackets(this IMethodSymbol meth
return $"<{method.GetGenericParameterString()}>";
}

public static string GetHarmonyPatchAnnotation(
this IMethodSymbol methodSymbol,
string typeFullName
)
{
// [HarmonyPatch(typeof(global::{ typeSymbol}), nameof(global::{ typeSymbol}.{ this.methodSymbol.Name}))]
string methodTypeArg = string.Empty;
string methodName = methodSymbol.Name;
if (methodSymbol.MethodKind == MethodKind.PropertyGet)
{
methodTypeArg = "global::HarmonyLib.MethodType.Getter";
methodName = methodName.Substring(4);
}
else if (methodSymbol.MethodKind == MethodKind.PropertySet)
{
methodTypeArg = "global::HarmonyLib.MethodType.Setter";
methodName = methodName.Substring(4);
}

//if (methodSymbol.ReturnType.IsTask() || methodSymbol.ReturnType.IsValueTask())
//{
// methodTypeArg = "global::HarmonyLib.MethodType.Async";
//}

if (!string.IsNullOrEmpty(methodTypeArg))
{
return $"[global::HarmonyLib.HarmonyPatch(typeof({typeFullName}), nameof({typeFullName}.{methodName}){methodTypeArg.AddPrefixIfNotEmpty(", ")})]";
}
else
{
return $"[global::HarmonyLib.HarmonyPatch(typeof({typeFullName}), nameof({typeFullName}.{methodName}){string.Join(", ", methodSymbol.Parameters.Select(p => p.Type.ToFullTypeString().AddOnIfNotEmpty("typeof(", ")"))).AddPrefixIfNotEmpty(", ")})]";
}
}

public static string GetPropertyName(this IMethodSymbol methodSymbol)
{
if (methodSymbol.MethodKind is MethodKind.PropertyGet or MethodKind.PropertySet)
Expand All @@ -196,18 +136,4 @@ public static string GetUniqueMethodName(this IMethodSymbol methodSymbol)
var uniqueMethodName = $"{methodName}_{string.Join("_", parameterTypes)}";
return uniqueMethodName;
}

public static string GetUniquePropertyNameIgnoringGetSet(this IMethodSymbol methodSymbol)
{
var methodName = methodSymbol.Name;
var parameterTypes = methodSymbol.Parameters.Select(p => p.Type.Name);
var uniqueMethodName = $"{methodName}_{string.Join("_", parameterTypes)}";

if (methodSymbol.MethodKind is MethodKind.PropertyGet or MethodKind.PropertySet)
{
return uniqueMethodName[4..];
}

return uniqueMethodName;
}
}
46 changes: 10 additions & 36 deletions src/MockMe.Generator/MockStoreGenerator.cs
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Text;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Text;
using MockMe.Abstractions;
using MockMe.Generator.Extensions;
using MockMe.Generator.MockGenerators.TypeGenerators;

Expand Down Expand Up @@ -64,12 +62,6 @@ internal static partial class {StoreClassName}
Dictionary<ITypeSymbol, string> createMockSymbolToNameDict = [];
foreach (var typeToMock in GetTypesToBeMocked(source.Left, source.Right))
{
string patchCall = "";
//if (typeToMock.TypeKind != TypeKind.Interface)
//{
// patchCall = "EnsurePatch();";
//}

string genericConstraint;
if (typeToMock.IsSealed)
{
Expand Down Expand Up @@ -115,10 +107,8 @@ out var typeToMockName
sourceBuilder.AppendLine(
@$"
[global::System.CodeDom.Compiler.GeneratedCode(""MockMe"", ""{MockMeVersion}"")]
public static global::MockMe.Generated.{typeToMock.ContainingNamespace}.{typeToMockName}Mock{genericArgs} {StoreMethodName}<T>(global::{typeToMock}? unusedInstance{(typeToMock.IsSealed ? "" : " = null")})
{genericConstraint}
public static global::MockMe.Generated.{typeToMock.ContainingNamespace}.{typeToMockName}Mock{genericArgs} {StoreMethodName}(global::{typeToMock}? unusedInstance)
{{
{patchCall}
return new();
}}"
);
Expand Down Expand Up @@ -175,15 +165,16 @@ ImmutableArray<InvocationExpressionSyntax> methods
method.Expression is MemberAccessExpressionSyntax memberAccess
&& memberAccess.Expression is IdentifierNameSyntax identifierName
&& identifierName.Identifier.Text == StoreClassName
&& memberAccess.Name is GenericNameSyntax genericName
&& genericName.TypeArgumentList.Arguments.Count == 1
&& genericName.Identifier.Text == StoreMethodName
&& method.ArgumentList.Arguments.Count == 1
&& method.ArgumentList.Arguments[0].Expression
is DefaultExpressionSyntax defaultExpression
//&& memberAccess.Name is GenericNameSyntax genericName
//&& genericName.TypeArgumentList.Arguments.Count == 1
//&& genericName.Identifier.Text == StoreMethodName
)
{
var model = compilation.GetSemanticModel(method.SyntaxTree);
var genericArgSymbol = model
.GetTypeInfo(genericName.TypeArgumentList.Arguments[0])
.Type;
var genericArgSymbol = model.GetTypeInfo(defaultExpression.Type).Type;

if (
genericArgSymbol is not null
Expand Down Expand Up @@ -212,26 +203,9 @@ namespace {NamespaceName}
{{
internal static partial class {StoreClassName}
{{
public static Mock<T> {StoreMethodName}<T>(global::{NamespaceName}.DummyClass unusedInstance)
where T : global::{NamespaceName}.DummyClass
{{
throw new NotImplementedException();
}}
private static bool isPatched;
private static readonly object LockObj = new();
private static void EnsurePatch()
public static object {StoreMethodName}(global::{NamespaceName}.DummyClass unusedInstance)
{{
lock (LockObj)
{{
if (!isPatched)
{{
var harmony = new global::HarmonyLib.Harmony(""com.mockme.patch"");
harmony.PatchAll();
isPatched = true;
}}
}}
throw new global::System.NotImplementedException();
}}
}}
}}
Expand Down
20 changes: 0 additions & 20 deletions src/MockMe.PostBuild/Extensions/GenericMethodInfoExtensions.cs

This file was deleted.

41 changes: 0 additions & 41 deletions src/MockMe.PostBuild/Extensions/TypeDefinitionExtensions.cs

This file was deleted.

8 changes: 1 addition & 7 deletions src/MockMe/MockCallTracker.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using MockMe.Extensions;
using MockMe.Mocks.ClassMemberMocks;

namespace MockMe;

Expand Down Expand Up @@ -53,12 +52,7 @@ private static TReturn? FindAndCallApplicableMemberMock<
continue;
}

var localReturn = CallMemberMockBase<
TReturn,
TOriginalArgCollection,
TCallback,
TReturnCall
>(
var localReturn = CallMemberMockBase(
argBag.Mock,
argCollection,
callbackAction,
Expand Down
6 changes: 3 additions & 3 deletions tests/MockMe.Tests.Overloads/ArgumentModifierTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ public class ArgumentModifierTests
[Fact]
public void OutKeyword_WhenConfiguredInReturnCall_ShouldSetTheCorrectValue()
{
var mock = Mock.Me<AllOverloads>(default(AllOverloads));
var mock = Mock.Me(default(AllOverloads));

mock.Setup.OutArgument(out _)
.Returns(args =>
Expand All @@ -28,7 +28,7 @@ public void OutKeyword_WhenConfiguredInReturnCall_ShouldSetTheCorrectValue()
[Fact]
public void OutKeyword_WhenConfiguredInCallbackCall_ShouldSetTheCorrectValue()
{
var mock = Mock.Me<AllOverloads>(default(AllOverloads));
var mock = Mock.Me(default(AllOverloads));

mock.Setup.OutArgument(out _).Callback(args => args.arg = 55).Returns(99);

Expand All @@ -43,7 +43,7 @@ public void OutKeyword_WhenConfiguredInCallbackCall_ShouldSetTheCorrectValue()
[Fact]
public void ParametersNotPassedByReference_ShouldNotHaveASetter()
{
var mock = Mock.Me<AllOverloads>(default(AllOverloads));
var mock = Mock.Me(default(AllOverloads));

var outParamType = typeof(AllOverloadsMockSetup.OutArgument_OutInt32Collection);
var regularParamType = typeof(AllOverloadsMockSetup.OutArgument_Int32Collection);
Expand Down
2 changes: 1 addition & 1 deletion tests/MockMe.Tests.Overloads/AsyncMethodOverloadTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public class AsyncMethodOverloadTests
[InlineData(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15)]
public async Task AsyncReturnOverload_CallbackAndAssertShouldWork(params int[] ints)
{
var mock = Mock.Me<AllOverloads>(null);
var mock = Mock.Me(default(AllOverloads));

int numCalls = 0;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public class AsyncOfTMethodOverloadsTests
[InlineData(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15)]
public async Task AsyncOfTReturnOverload_CallbackAndAssertShouldWork(params int[] ints)
{
var mock = Mock.Me<AllOverloads>(default(AllOverloads));
var mock = Mock.Me(default(AllOverloads));

int numCalls = 0;

Expand Down
8 changes: 4 additions & 4 deletions tests/MockMe.Tests.Overloads/IndexerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ public class IndexerTests
[Fact]
public void IndexerGetForInt_ReturnsAndCallbackShouldWork()
{
var mock = Mock.Me<AllOverloads>(default(AllOverloads));
var mock = Mock.Me(default(AllOverloads));

int numCalls = 0;
mock.Setup[Arg.Any<int>()].Get().Callback(() => numCalls++).Returns("hello indexer");
Expand All @@ -28,7 +28,7 @@ public void IndexerGetForInt_ReturnsAndCallbackShouldWork()
[Fact]
public void IndexerSetForInt_ReturnsAndCallbackShouldWork()
{
var mock = Mock.Me<AllOverloads>(default(AllOverloads));
var mock = Mock.Me(default(AllOverloads));

int numCalls = 0;
mock.Setup[Arg.Any<int>()].Set(Arg.Any()).Callback(() => numCalls++);
Expand All @@ -48,7 +48,7 @@ public void IndexerSetForInt_ReturnsAndCallbackShouldWork()
[Fact]
public void IndexerGetForString_ReturnsAndCallbackShouldWork()
{
var mock = Mock.Me<AllOverloads>(default(AllOverloads));
var mock = Mock.Me(default(AllOverloads));

int numCalls = 0;
mock.Setup[Arg.Any<string>()].Get().Callback(() => numCalls++).Returns(99);
Expand All @@ -66,7 +66,7 @@ public void IndexerGetForString_ReturnsAndCallbackShouldWork()
[Fact]
public void SetOnlyIndexer_AssertAndCallbackShouldWork()
{
var mock = Mock.Me<AllOverloads>(default(AllOverloads));
var mock = Mock.Me(default(AllOverloads));

int numCalls = 0;
mock.Setup[Arg.Any<double>()].Set(Arg.Any()).Callback(() => numCalls++);
Expand Down
Loading

0 comments on commit c59d818

Please sign in to comment.