Skip to content

Commit

Permalink
Mock Db Context added to SqlLite DbContext (#27)
Browse files Browse the repository at this point in the history
* Allow GetDbContext to create context without option interaction or new restraint.

* Options, dbConnection options added to simplified GetDbContext

* add replace to add type of dbContext creation

* Add mockable DbContext

* Cleanup

* Split mocker class

* Cleanup

* Retain original functionality with SqlLite Db.

* Undo accidently change to MockerTestBase

* Undo accidently change

* Add EnsureNullCheckThrown extension

* Update readme

* Support DbContext with parameterized constructors

* Fix callbase

* require set overrides to be virtual. Otherwise it would throw an error.

* Misc / Fix dbset named Set method
  • Loading branch information
cwinland authored Nov 3, 2023
1 parent 407e08d commit 03374b5
Show file tree
Hide file tree
Showing 24 changed files with 2,828 additions and 809 deletions.
1,335 changes: 1,335 additions & 0 deletions .editorconfig

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions FastMoq.Core/AssemblyInfo.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

// In SDK-style projects such as this one, several assembly attributes that were historically
Expand All @@ -15,3 +16,4 @@
// The following GUID is for the ID of the typelib if this project is exposed to COM.

[assembly: Guid("d18df962-fe1d-4f9b-a1df-adc142bf16a0")]
[assembly: InternalsVisibleTo("FastMoq.Tests")]
255 changes: 213 additions & 42 deletions FastMoq.Core/Extensions/TestClassExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Linq.Expressions;
using System.Collections;
using System.Linq.Expressions;
using System.Reflection;

namespace FastMoq.Extensions
Expand All @@ -9,12 +10,18 @@ namespace FastMoq.Extensions
public static class TestClassExtensions
{
/// <summary>
/// ForEach for <see cref="IEnumerable{T}"/>.
/// Gets the default value.
/// </summary>
/// <typeparam name="T">Type of item.</typeparam>
/// <param name="iEnumerable">The <see cref="IEnumerable{T}"/>.</param>
/// <param name="action">The action.</param>
internal static void ForEach<T>(this IEnumerable<T> iEnumerable, Action<T> action) => iEnumerable.ToList().ForEach(action);
/// <param name="type">The type.</param>
/// <returns><see cref="Nullable{T}" />.</returns>
public static object? GetDefaultValue(this Type type) => type switch
{
{ FullName: "System.Uri" } => new UriBuilder { Scheme = "http", Host = "localhost" }.Uri,
{ FullName: "System.String" } => string.Empty,
_ when typeof(IEnumerable).IsAssignableFrom(type) => Array.CreateInstance(type.GetElementType() ?? typeof(object), 0),
{ IsClass: true } => null,
_ => Activator.CreateInstance(type),
};

/// <summary>
/// Gets the field.
Expand Down Expand Up @@ -57,7 +64,7 @@ public static FieldInfo GetFieldInfo<TType>(this object _, string name)
/// <param name="obj">The object.</param>
/// <param name="field">The field.</param>
/// <returns>System.Nullable&lt;T&gt;.</returns>
public static T? GetFieldValue<T>(this object? obj, FieldInfo field) => (T?)field.GetValue(obj);
public static T? GetFieldValue<T>(this object? obj, FieldInfo field) => (T?) field.GetValue(obj);

/// <summary>
/// Gets the field value.
Expand All @@ -78,24 +85,8 @@ public static FieldInfo GetFieldInfo<TType>(this object _, string name)
/// <param name="_">The object.</param>
/// <param name="memberLambda">The member lambda.</param>
/// <returns>System.Nullable&lt;TValue&gt;.</returns>
public static MemberInfo GetMember<T, TValue>(this T _, Expression<Func<T, TValue>> memberLambda) => memberLambda.GetMemberExpression().Member;

/// <summary>
/// Gets the name of the member.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <typeparam name="TValue">The type of the t value.</typeparam>
/// <param name="_">The .</param>
/// <param name="memberLambda">The member lambda.</param>
/// <returns>System.String.</returns>
public static string GetMemberName<T, TValue>(this T _, Expression<Func<T, TValue>> memberLambda) => memberLambda.GetMemberExpression().Member.Name;

/// <summary>
/// Gets the name of the member.
/// </summary>
/// <param name="memberLambda">The member lambda.</param>
/// <returns>System.String.</returns>
public static string GetMemberName(this Expression memberLambda) => memberLambda.GetMemberExpressionInternal().Member.Name;
public static MemberInfo GetMember<T, TValue>(this T _, Expression<Func<T, TValue>> memberLambda) =>
memberLambda.GetMemberExpression().Member;

/// <summary>
/// Gets the member expression.
Expand All @@ -112,22 +103,23 @@ public static FieldInfo GetFieldInfo<TType>(this object _, string name)
/// <returns>MemberExpression.</returns>
public static MemberExpression GetMemberExpression(this Expression method) => method.GetMemberExpressionInternal();

private static MemberExpression GetMemberExpressionInternal(this Expression method)
{
if (method is not LambdaExpression lambda)
{
throw new ArgumentNullException(nameof(method));
}

var memberExpr = lambda.Body.NodeType switch
{
ExpressionType.Convert => ((UnaryExpression)lambda.Body).Operand as MemberExpression,
ExpressionType.MemberAccess => lambda.Body as MemberExpression,
_ => null
};
/// <summary>
/// Gets the name of the member.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <typeparam name="TValue">The type of the t value.</typeparam>
/// <param name="_">The .</param>
/// <param name="memberLambda">The member lambda.</param>
/// <returns>System.String.</returns>
public static string GetMemberName<T, TValue>(this T _, Expression<Func<T, TValue>> memberLambda) =>
memberLambda.GetMemberExpression().Member.Name;

return memberExpr ?? throw new ArgumentNullException(nameof(method));
}
/// <summary>
/// Gets the name of the member.
/// </summary>
/// <param name="memberLambda">The member lambda.</param>
/// <returns>System.String.</returns>
public static string GetMemberName(this Expression memberLambda) => memberLambda.GetMemberExpressionInternal().Member.Name;

/// <summary>
/// Gets the method.
Expand All @@ -149,7 +141,8 @@ private static MemberExpression GetMemberExpressionInternal(this Expression meth
/// <param name="defaultValue">The default value.</param>
/// <param name="args">The arguments.</param>
/// <returns>System.Nullable&lt;System.Object&gt;.</returns>
public static object? GetMethodValue<TObject>(this TObject obj, string name, object? defaultValue = null, params object[] args) where TObject : class?
public static object? GetMethodValue<TObject>(this TObject obj, string name, object? defaultValue = null, params object[] args)
where TObject : class?
=> obj.GetMethod(name)?.Invoke(obj, args) ?? defaultValue;

/// <summary>
Expand Down Expand Up @@ -194,5 +187,183 @@ public static void SetFieldValue<TObject>(this TObject obj, string name, object?
/// <param name="value">The value.</param>
public static void SetPropertyValue<TObject>(this TObject obj, string name, object? value) where TObject : class? =>
obj.GetProperty(name)?.SetValue(obj, value);

/// <summary>
/// ForEach for <see cref="IEnumerable{T}" />.
/// </summary>
/// <typeparam name="T">Type of item.</typeparam>
/// <param name="iEnumerable">The <see cref="IEnumerable{T}" />.</param>
/// <param name="action">The action.</param>
internal static void ForEach<T>(this IEnumerable<T> iEnumerable, Action<T> action) => iEnumerable.ToList().ForEach(action);

/// <summary>
/// Gets the injection fields.
/// </summary>
/// <param name="type">The type.</param>
/// <param name="attributeType">Override attribute type.</param>
/// <returns><see cref="IEnumerable{T}" />.</returns>
internal static IEnumerable<FieldInfo> GetInjectionFields(this Type type, Type? attributeType = null) =>
type
.GetRuntimeFields()
.Where(x => x.CustomAttributes.Any(y =>
y.AttributeType == attributeType ||
y.AttributeType.Name.Equals("InjectAttribute", StringComparison.OrdinalIgnoreCase)
)
);

/// <summary>
/// Gets the injection properties.
/// </summary>
/// <param name="type">The type.</param>
/// <param name="attributeType">Override attribute type.</param>
/// <returns><see cref="IEnumerable{T}" />.</returns>
internal static IEnumerable<PropertyInfo> GetInjectionProperties(this Type type, Type? attributeType = null) =>
type
.GetRuntimeProperties()
.Where(x => x.CustomAttributes.Any(y =>
y.AttributeType == attributeType ||
y.AttributeType.Name.Equals("InjectAttribute", StringComparison.OrdinalIgnoreCase)
)
);

/// <summary>
/// Gets the member expression internal.
/// </summary>
/// <param name="method">The method.</param>
/// <returns>MemberExpression of the member expression internal.</returns>
/// <exception cref="ArgumentNullException">nameof(method)</exception>
internal static MemberExpression GetMemberExpressionInternal(this Expression method)
{
if (method is not LambdaExpression lambda)
{
throw new ArgumentNullException(nameof(method));
}

var memberExpr = lambda.Body.NodeType switch
{
ExpressionType.Convert => ((UnaryExpression) lambda.Body).Operand as MemberExpression,
ExpressionType.MemberAccess => lambda.Body as MemberExpression,
_ => null,
};

return memberExpr ?? throw new ArgumentNullException(nameof(method));
}

/// <summary>
/// Determines whether [is nullable type] [the specified type].
/// </summary>
/// <param name="type">The type.</param>
/// <returns><c>true</c> if [is nullable type] [the specified type]; otherwise, <c>false</c>.</returns>
internal static bool IsNullableType(this Type type) =>
type.IsClass || (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>));

/// <summary>
/// Returns true if the argument list == 0 or the types match the constructor exactly.
/// </summary>
/// <param name="type">Type which the constructor is from.</param>
/// <param name="info">Parameter information.</param>
/// <param name="instanceParameterValues">Optional arguments.</param>
/// <returns><c>true</c> if [is valid constructor] [the specified information]; otherwise, <c>false</c>.</returns>
internal static bool IsValidConstructor(this Type type, ConstructorInfo info, params object?[] instanceParameterValues)
{
var paramList = info.GetParameters().ToList();

if (instanceParameterValues.Length == 0)
{
return paramList.All(x => x.ParameterType != type);
}

if (instanceParameterValues.Length > paramList.Count)
{
return false;
}

var isValid = true;

for (var i = 0; i < instanceParameterValues.Length; i++)
{
var paramType = paramList[i].ParameterType;
var instanceType = instanceParameterValues[i]?.GetType();

isValid &= (instanceType == null && (paramType.IsNullableType() || paramType.IsInterface)) ||
(instanceType != null && paramType.IsAssignableFrom(instanceType));
}

return isValid;
}

/// <summary>
/// Returns true if the argument list == 0 or the types match the constructor exactly.
/// </summary>
/// <param name="info">Parameter information.</param>
/// <param name="instanceParameterValues">Optional arguments.</param>
/// <returns><c>true</c> if [is valid constructor] [the specified information]; otherwise, <c>false</c>.</returns>
internal static bool IsValidConstructorByType(this ConstructorInfo info, params Type?[] instanceParameterValues)
{
if (instanceParameterValues.Length == 0)
{
return true;
}

var paramList = info.GetParameters().ToList();

if (instanceParameterValues.Length != paramList.Count)
{
return false;
}

var isValid = true;

for (var i = 0; i < paramList.Count; i++)
{
var paramType = paramList[i].ParameterType;
var instanceType = instanceParameterValues[i];

isValid &= paramType.IsAssignableFrom(instanceType);
}

return isValid;
}

/// <summary>
/// Throws the already exists.
/// </summary>
/// <param name="type">The type.</param>
/// <exception cref="System.ArgumentException"></exception>
internal static void ThrowAlreadyExists(this Type type) => throw new ArgumentException($"{type} already exists.");

/// <summary>
/// Ensures the null check thrown.
/// </summary>
/// <param name="action">The action.</param>
/// <param name="parameterName">Name of the parameter.</param>
/// <param name="constructorName">Name of the constructor.</param>
/// <param name="output">The output.</param>
public static void EnsureNullCheckThrown(this Action action, string parameterName, string? constructorName = "", Action<string>? output = null)
{
try
{
output?.Invoke($"Testing {constructorName}\n - {parameterName}");

try
{
action();
}
catch (ArgumentNullException ex)
{
if (!ex.Message.Contains(parameterName))
{
throw;
}
}

output?.Invoke($"Passed {parameterName}");
}
catch
{
output?.Invoke($"Failed {parameterName}");
throw;
}
}
}
}
}
2 changes: 1 addition & 1 deletion FastMoq.Core/FastMoq.Core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@
<ExcludeAssets>analyzers</ExcludeAssets>
</PackageReference>
<PackageReference Include="Moq" Version="4.18.*" />
<PackageReference Include="coverlet.collector" Version="3.2.0">
<PackageReference Include="coverlet.collector" Version="6.0.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; buildtransitive</IncludeAssets>
<ExcludeAssets>analyzers</ExcludeAssets>
Expand Down
Loading

0 comments on commit 03374b5

Please sign in to comment.