Skip to content

Commit

Permalink
Restructure (#35)
Browse files Browse the repository at this point in the history
* restructure

* help files

* Consolidate NonPublic duplicate constructor methods

* Implement fixes to ConstructorHistory

* Add History Tests and fixes

* ConstructorHistoryTests and refinement.

* Cleanup

* Add Get Protected Mock Generic

* try refactor

* Update except bunit

* Update Bunit

* Update comments and help API

* Update codeql.yml
  • Loading branch information
cwinland authored Apr 10, 2024
1 parent 51af2eb commit 4d88040
Show file tree
Hide file tree
Showing 631 changed files with 24,434 additions and 54,541 deletions.
2 changes: 1 addition & 1 deletion .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -892,7 +892,7 @@ dotnet_diagnostic.S3963.severity = warning
dotnet_diagnostic.S3967.severity = warning

# S4018: Minor :: Generic methods should provide type parameters
dotnet_diagnostic.S4018.severity = warning
dotnet_diagnostic.S4018.severity = silent

# S4022: Minor :: Enumerations should have "Int32" storage
dotnet_diagnostic.S4022.severity = silent
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
# supported CodeQL languages.
#
name: "CodeQL"

Expand Down
37 changes: 37 additions & 0 deletions FastMoq.Core/Extensions/MockerBooleanExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using System.IO.Abstractions;

namespace FastMoq.Extensions
{
/// <summary>
/// Mocker Boolean Extensions Class.
/// </summary>
public static class MockerBooleanExtensions
{
/// <summary>
/// Determines whether this instance contains a Mock of <c>T</c>.
/// </summary>
/// <typeparam name="T">The Mock <see cref="T:Type" />, usually an interface.</typeparam>
/// <param name="mocker">The mocker.</param>
/// <returns><c>true</c> if the Mock exists for the given type; otherwise, <c>false</c>.</returns>
/// <exception cref="System.ArgumentNullException">type is null.</exception>
/// <exception cref="System.ArgumentException">type must be a class. - type</exception>
public static bool Contains<T>(this Mocker mocker) where T : class => mocker.Contains(typeof(T));

/// <summary>
/// Determines whether this instance contains the object.
/// </summary>
/// <param name="mocker">The mocker.</param>
/// <param name="type">The type.</param>
/// <returns><c>true</c> if [contains] [the specified type]; otherwise, <c>false</c>.</returns>
/// <exception cref="System.ArgumentNullException"></exception>
/// <exception cref="System.ArgumentException">type must be a class. - type</exception>
public static bool Contains(this Mocker mocker, Type type)
{
ArgumentNullException.ThrowIfNull(type);

return !type.IsClass && !type.IsInterface
? throw new ArgumentException("type must be a class.", nameof(type))
: mocker.mockCollection.Exists(x => x.Type == type);
}
}
}
269 changes: 269 additions & 0 deletions FastMoq.Core/Extensions/MockerCreationExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,269 @@
using FastMoq.Models;
using Microsoft.EntityFrameworkCore;
using Moq;
using System.Reflection;

namespace FastMoq.Extensions
{
/// <summary>
/// Mocker Create Extensions
/// </summary>
public static class MockerCreationExtensions
{
/// <summary>
/// Creates an instance of <c>T</c>. Parameter data allows matching of constructors by type and uses those values in the creation of the instance.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <typeparam name="TParam1">The type of the t param1.</typeparam>
/// <param name="mocker">The mocker.</param>
/// <param name="data">The data.</param>
/// <returns>T.</returns>
public static T? CreateInstance<T, TParam1>(this Mocker mocker, Dictionary<Type, object?> data) where T : class => mocker.CreateInstanceInternal<T>(
model => mocker.FindConstructorByType(model.InstanceType, true, typeof(TParam1)), data
);

/// <summary>
/// Creates an instance of <c>T</c>. Parameter data allows matching of constructors by type and uses those values in the creation of the instance.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <typeparam name="TParam1">The type of the t param1.</typeparam>
/// <typeparam name="TParam2">The type of the t param2.</typeparam>
/// <param name="mocker">The mocker.</param>
/// <param name="data">The data.</param>
/// <returns>T.</returns>
public static T? CreateInstance<T, TParam1, TParam2>(this Mocker mocker, Dictionary<Type, object?> data) where T : class => mocker.CreateInstanceInternal<T>(
model => mocker.FindConstructorByType(model.InstanceType, true, typeof(TParam1), typeof(TParam2)),
data
);

/// <summary>
/// Creates an instance of <c>T</c>. Parameter data allows matching of constructors by type and uses those values in the creation of the instance.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <typeparam name="TParam1">The type of the t param1.</typeparam>
/// <typeparam name="TParam2">The type of the t param2.</typeparam>
/// <typeparam name="TParam3">The type of the t param3.</typeparam>
/// <param name="mocker">The mocker.</param>
/// <param name="data">The data.</param>
/// <returns>T.</returns>
public static T? CreateInstance<T, TParam1, TParam2, TParam3>(this Mocker mocker, Dictionary<Type, object?> data) where T : class =>
mocker.CreateInstanceInternal<T>(
model => mocker.FindConstructorByType(model.InstanceType, true, typeof(TParam1), typeof(TParam2), typeof(TParam3)),
data
);

/// <summary>
/// Creates an instance of <c>T</c>. Parameter data allows matching of constructors by type and uses those values in the creation of the instance.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <typeparam name="TParam1">The type of the t param1.</typeparam>
/// <typeparam name="TParam2">The type of the t param2.</typeparam>
/// <typeparam name="TParam3">The type of the t param3.</typeparam>
/// <typeparam name="TParam4">The type of the t param4.</typeparam>
/// <param name="mocker">The mocker.</param>
/// <param name="data">The data.</param>
/// <returns>T.</returns>
public static T? CreateInstance<T, TParam1, TParam2, TParam3, TParam4>(this Mocker mocker, Dictionary<Type, object?> data) where T : class =>
mocker.CreateInstanceInternal<T>(
model => mocker.FindConstructorByType(model.InstanceType,
true,
typeof(TParam1),
typeof(TParam2),
typeof(TParam3),
typeof(TParam4)
),
data
);

/// <summary>
/// Creates an instance of <c>T</c>. Parameter data allows matching of constructors by type and uses those values in the creation of the instance.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <typeparam name="TParam1">The type of the t param1.</typeparam>
/// <typeparam name="TParam2">The type of the t param2.</typeparam>
/// <typeparam name="TParam3">The type of the t param3.</typeparam>
/// <typeparam name="TParam4">The type of the t param4.</typeparam>
/// <typeparam name="TParam5">The type of the t param5.</typeparam>
/// <param name="mocker">The mocker.</param>
/// <param name="data">The data.</param>
/// <returns>T.</returns>
public static T? CreateInstance<T, TParam1, TParam2, TParam3, TParam4, TParam5>(this Mocker mocker, Dictionary<Type, object?> data) where T : class =>
mocker.CreateInstanceInternal<T>(
model => mocker.FindConstructorByType(model.InstanceType,
true,
typeof(TParam1),
typeof(TParam2),
typeof(TParam3),
typeof(TParam4),
typeof(TParam5)
),
data
);

/// <summary>
/// Creates the mock internal.
/// </summary>
/// <param name="mocker">The mocker.</param>
/// <param name="type">The type.</param>
/// <param name="parameterList">The constructor.</param>
/// <param name="isNonPublic">if set to <c>true</c> [is non public].</param>
/// <returns>Creates the mock internal.</returns>
/// <exception cref="System.ApplicationException">Cannot create instance.</exception>
public static Mock CreateMockInternal(this Mocker mocker, Type type, IReadOnlyCollection<object?> parameterList, bool isNonPublic = false)
{
var flags = isNonPublic
? BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.CreateInstance
: BindingFlags.Instance | BindingFlags.Public | BindingFlags.CreateInstance;

var isDbContext = type.IsAssignableTo(typeof(DbContext));
var newType = isDbContext ? typeof(DbContextMock<>).MakeGenericType(type) : typeof(Mock<>).MakeGenericType(type);

// Execute new Mock with Loose Behavior and arguments from constructor, if applicable.
var parameters = new List<object?> { mocker.Strict ? MockBehavior.Strict : MockBehavior.Loose };
parameterList.ForEach(parameters.Add);

return Activator.CreateInstance(newType,
flags,
null,
parameters.ToArray(),
null,
null
) as Mock ??
throw new ApplicationException("Cannot create instance.");
}


internal static object GetSafeMockObject(this Mocker mocker, Mock mock)
{
try
{
return mock.Object.RaiseIfNull();
}
catch (TargetInvocationException ex)
{
mocker.exceptionLog.Add(ex.Message);
ex.ThrowIfCastleMethodAccessException(); // Throw actual error.
throw; // Bubble up since not a CastleAccessException
}
catch (MethodAccessException ex)
{
mocker.exceptionLog.Add(ex.Message);
ex.ThrowIfCastleMethodAccessException(); // Throw actual error.
throw; // Bubble up since not a CastleAccessException
}
catch (Exception ex)
{
mocker.exceptionLog.Add(ex.Message);
throw;
}
}

internal static ConstructorModel GetTypeConstructor(this Mocker mocker, Type type, bool nonPublic, object?[] args)
{
var constructor = new ConstructorModel(null, args);

try
{
if (!type.IsInterface)
{
// Find the best constructor and build the parameters.
constructor = args.Length > 0 || nonPublic ? mocker.FindConstructor(type, true, args) : mocker.FindConstructor(true, type, nonPublic);
}
}
catch (Exception ex)
{
mocker.exceptionLog.Add(ex.Message);
}

if (constructor.ConstructorInfo == null && !mocker.HasParameterlessConstructor(type))
{
try
{
constructor = mocker.GetConstructors(type, nonPublic).MinBy(x => x.ConstructorInfo?.GetParameters().Length ?? 0) ?? constructor;
}
catch (Exception ex)
{
// It's okay if this fails.
mocker.exceptionLog.Add(ex.Message);
}
}

return constructor;
}

/// <summary>
/// Create an instance using the constructor by the function.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="mocker">The mocker.</param>
/// <param name="constructorFunc">The constructor function.</param>
/// <param name="data">The arguments.</param>
/// <returns>T.</returns>
internal static T? CreateInstanceInternal<T>(this Mocker mocker, Func<IInstanceModel, ConstructorInfo> constructorFunc, Dictionary<Type, object?>? data) where T : class
{
var type = typeof(T).IsInterface ? mocker.GetTypeFromInterface<T>() : new InstanceModel<T>();

if (type.CreateFunc != null)
{
return (T) type.CreateFunc.Invoke(mocker);
}

data ??= new();
var constructor = constructorFunc(type);

var args = mocker.GetArgData(constructor, data);

return mocker.CreateInstanceInternal<T>(constructor, args);
}

/// <summary>
/// Creates the instance.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="mocker">The mocker.</param>
/// <param name="constructorModel">The constructor model.</param>
/// <returns>T.</returns>
internal static T? CreateInstanceInternal<T>(this Mocker mocker, ConstructorModel constructorModel) where T : class =>
mocker.CreateInstanceInternal<T>(constructorModel.ConstructorInfo, constructorModel.ParameterList);

/// <summary>
/// Creates the instance.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="mocker">The mocker.</param>
/// <param name="info">The information.</param>
/// <param name="args">The arguments.</param>
/// <returns>T.</returns>
internal static T? CreateInstanceInternal<T>(this Mocker mocker, ConstructorInfo? info, params object?[] args) where T : class =>
mocker.CreateInstanceInternal(typeof(T), info, args) as T;

/// <summary>
/// Creates the instance internal.
/// </summary>
/// <param name="mocker">The mocker.</param>
/// <param name="type">The type.</param>
/// <param name="info">The information.</param>
/// <param name="args">The arguments.</param>
/// <returns>object?.</returns>
internal static object? CreateInstanceInternal(this Mocker mocker, Type type, ConstructorInfo? info, params object?[] args)
{
mocker.ConstructorHistory.AddOrUpdate(type, new ConstructorModel(info, args));
var paramList = info?.GetParameters().ToList() ?? new();
var newArgs = args.ToList();

if (args.Length < paramList.Count)
{
for (var i = args.Length; i < paramList.Count; i++)
{
var p = paramList[i];
newArgs.Add(p.IsOptional ? null : mocker.GetParameter(p.ParameterType));
}
}

var obj = mocker.AddInjections(info?.Invoke(newArgs.ToArray()));
return mocker.InnerMockResolution ? mocker.AddProperties(type, obj) : obj;
}


}
}
Loading

0 comments on commit 4d88040

Please sign in to comment.