diff --git a/FastMoq.Core/Extensions/MockerCreationExtensions.cs b/FastMoq.Core/Extensions/MockerCreationExtensions.cs
index 913fa1e..a5eccf4 100644
--- a/FastMoq.Core/Extensions/MockerCreationExtensions.cs
+++ b/FastMoq.Core/Extensions/MockerCreationExtensions.cs
@@ -1,6 +1,7 @@
using FastMoq.Models;
using Microsoft.EntityFrameworkCore;
using Moq;
+using System.Linq.Expressions;
using System.Reflection;
namespace FastMoq.Extensions
@@ -100,16 +101,28 @@ public static class MockerCreationExtensions
data
);
+ ///
+ /// Creates a mock given the Type of Mock . Properties will be stubbed and have default setups.
+ ///
+ /// Type of Mock
+ /// The mocker.
+ /// if set to true , indicates if non-public constructors should be searched.
+ /// This is designed for interface mocks or concrete mocks without parameters.
+ /// Cannot create instance of Mock.
+ public static Mock CreateMockInternal(this Mocker mocker, bool isNonPublic = true) where T : class =>
+ (Mock)mocker.CreateMockInternal(typeof(T), new List(), true);
+
///
/// Creates the mock internal.
///
/// The mocker.
/// The type.
- /// The constructor.
- /// if set to true [is non public].
- /// Creates the mock internal.
- /// Cannot create instance.
- public static Mock CreateMockInternal(this Mocker mocker, Type type, IReadOnlyCollection parameterList, bool isNonPublic = false)
+ /// The constructor parameters.
+ /// if set to true , indicates if non-public constructors should be searched.
+ /// if set to true , attempts to setup internal mocks and properties.
+ /// Parameter list only works if the type is concrete. Otherwise, pass an empty list.
+ /// Cannot create instance of Mock.
+ public static Mock CreateMockInternal(this Mocker mocker, Type type, IReadOnlyCollection? parameterList = null, bool isNonPublic = false, bool setupMock = true)
{
var flags = isNonPublic
? BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.CreateInstance
@@ -120,18 +133,31 @@ public static Mock CreateMockInternal(this Mocker mocker, Type type, IReadOnlyCo
// Execute new Mock with Loose Behavior and arguments from constructor, if applicable.
var parameters = new List { mocker.Strict ? MockBehavior.Strict : MockBehavior.Loose };
+ parameterList ??= new List();
parameterList.ForEach(parameters.Add);
- return Activator.CreateInstance(newType,
- flags,
- null,
- parameters.ToArray(),
- null,
- null
- ) as Mock ??
- throw new ApplicationException("Cannot create instance.");
+ var instance = Activator.CreateInstance(newType,
+ flags,
+ null,
+ parameters.ToArray(),
+ null,
+ null
+ ) as Mock ??
+ throw CannotCreateMock(type);
+
+ if (setupMock)
+ {
+ mocker.SetupMock(type, instance);
+ }
+
+ instance.RaiseIfNull();
+ return instance;
}
+ private static ApplicationException CannotCreateMock(Type type)
+ {
+ return new ApplicationException($"Cannot create instance of 'Mock<{type.Name}>'.");
+ }
internal static object GetSafeMockObject(this Mocker mocker, Mock mock)
{
@@ -263,5 +289,60 @@ internal static ConstructorModel GetTypeConstructor(this Mocker mocker, Type typ
var obj = mocker.AddInjections(info?.Invoke(newArgs.ToArray()));
return mocker.InnerMockResolution ? mocker.AddProperties(type, obj) : obj;
}
+
+ ///
+ /// Setups the mock for given property info.
+ ///
+ /// The type of mock.
+ /// The mock.
+ /// The property information.
+ /// The value.
+ public static void SetupMockProperty(this Mock mock, PropertyInfo propertyInfo, object value) where TMock : class
+ {
+ // Create a parameter expression for the object instance of type TMock
+ var instanceParam = Expression.Parameter(typeof(TMock), "instance");
+
+ // Create an expression to access the property
+ var propertyAccess = Expression.Property(instanceParam, propertyInfo);
+
+ // Create a lambda expression that represents the getter
+ var getterExpression = Expression.Lambda>(propertyAccess, instanceParam);
+
+ // Set up the mock to return the provided value for the property getter
+ mock.Setup(getterExpression).Returns(value);
+ }
+
+ public static void SetupMockProperty(this Mock mock, Expression> propertyExpression, object value)
+ where TMock : class
+ {
+ var propertyInfo = propertyExpression.GetPropertyInfo();
+ mock.SetupMockProperty(propertyInfo, value);
+ }
+
+ internal static PropertyInfo GetPropertyInfo(this Expression> propertyExpression)
+ {
+ if (propertyExpression.Body is MemberExpression memberExpression)
+ {
+ if (memberExpression.Member is PropertyInfo propertyInfo)
+ {
+ return propertyInfo;
+ }
+ }
+
+ throw new ArgumentException("Expression is not a property access.", nameof(propertyExpression));
+ }
+
+ ///
+ /// Setups the mock for given property name.
+ ///
+ /// The type of the t mock.
+ /// The mock.
+ /// Name of the property.
+ /// The value.
+ public static void SetupMockProperty(this Mock mock, string propertyName, object value) where TMock : class
+ {
+ var propertyInfo = typeof(TMock).GetProperty("Headers");
+ mock.SetupMockProperty(propertyInfo, value);
+ }
}
}
diff --git a/FastMoq.Core/Extensions/TestClassExtensions.cs b/FastMoq.Core/Extensions/TestClassExtensions.cs
index 5608986..e8244cc 100644
--- a/FastMoq.Core/Extensions/TestClassExtensions.cs
+++ b/FastMoq.Core/Extensions/TestClassExtensions.cs
@@ -609,7 +609,7 @@ internal static List GetTestedConstructors(this Mocker mocker,
try
{
// Test Constructor.
- var mock = mocker.CreateMockInternal(type, constructor.ParameterList);
+ var mock = mocker.CreateMockInternal(type, constructor.ParameterList, setupMock: false);
_ = mock.Object;
validConstructors.Add(constructor);
}
diff --git a/FastMoq.Core/FastMoq.Core.csproj b/FastMoq.Core/FastMoq.Core.csproj
index 1e25237..d32d23e 100644
--- a/FastMoq.Core/FastMoq.Core.csproj
+++ b/FastMoq.Core/FastMoq.Core.csproj
@@ -43,15 +43,15 @@
+
+
+
+
-
-
-
-
diff --git a/FastMoq.Core/Mocker.cs b/FastMoq.Core/Mocker.cs
index 2ef4a3b..6bab081 100644
--- a/FastMoq.Core/Mocker.cs
+++ b/FastMoq.Core/Mocker.cs
@@ -55,6 +55,9 @@ public class Mocker
{
#region Fields
+ public const string SETUP_ALL_PROPERTIES_METHOD_NAME = "SetupAllProperties";
+ public const string SETUP = "Setup";
+
///
/// The virtual mock file system that is used by mocker unless overridden with the property.
///
@@ -191,21 +194,19 @@ public T AddInjections(T obj, Type? referenceType = null) where T : class?
///
public void AddFileSystemAbstractionMapping()
{
- this
- .AddType()
- .AddType()
- .AddType()
- .AddType()
- .AddType()
- .AddType()
- .AddType()
- .AddType()
- .AddType()
- .AddType()
- .AddType()
- .AddType()
- .AddType()
- ;
+ AddType()
+ .AddType()
+ .AddType()
+ .AddType()
+ .AddType()
+ .AddType()
+ .AddType()
+ .AddType()
+ .AddType()
+ .AddType()
+ .AddType()
+ .AddType()
+ .AddType();
}
///
@@ -574,8 +575,6 @@ public Mock CreateMockInstance(bool nonPublic = false, params object?[] ar
/// Type must be a class or interface., nameof(type)
public Mock CreateMockInstance(Type type, bool nonPublic = false, params object?[] args)
{
- const string SETUP_ALL_PROPERTIES_METHOD_NAME = "SetupAllProperties";
-
if (type == null || (!type.IsClass && !type.IsInterface))
{
throw new ArgumentException("Type must be a class or interface.", nameof(type));
@@ -585,18 +584,8 @@ public Mock CreateMockInstance(Type type, bool nonPublic = false, params object?
var oMock = this.CreateMockInternal(type, constructor.ParameterList, nonPublic);
- if (!Strict)
- {
- InvokeMethod(null, SETUP_ALL_PROPERTIES_METHOD_NAME, true, oMock);
-
- if (InnerMockResolution)
- {
- AddProperties(type, this.GetSafeMockObject(oMock));
- }
- }
-
- AddInjections(this.GetSafeMockObject(oMock), GetTypeModel(type)?.InstanceType ?? type);
-
+ SetupMock(type, oMock);
+ oMock.RaiseIfNull();
return oMock;
}
@@ -800,32 +789,6 @@ public static List GetList(int count, Func? func) =>
GetMock().Protected()
?.Setup>(methodOrPropertyName, args ?? Array.Empty());
- ///
- /// Gets the method argument data.
- ///
- /// The method.
- /// The data.
- /// System.Nullable<System.Object>[].
- ///// method
- //public object?[] GetMethodArgData(MethodInfo method, Dictionary? data = null)
- //{
- // if (method == null)
- // {
- // throw new ArgumentNullException(nameof(method));
- // }
-
- // var args = new List();
-
- // method.GetParameters().ToList().ForEach(p =>
- // args.Add(data?.Any(x => x.Key == p.ParameterType) ?? false
- // ? data.First(x => x.Key == p.ParameterType).Value
- // : GetParameter(p.ParameterType)
- // )
- // );
-
- // return args.ToArray();
- //}
-
///
/// Gets the method argument data.
///
@@ -912,7 +875,7 @@ public Task GetMockAsync(Func, Task> mockFunc, params object?[] args)
}
///
- /// Gets of creates the mock of type .
+ /// Gets of creates the of type .
///
/// The type.
/// The arguments used to find the correct constructor for a class.
@@ -1050,7 +1013,7 @@ public DbContextMock GetMockDbContext() where TDbContext
mock.CallBase = true;
}
- var mockObject = mock.Object;
+ var mockObject = this.GetSafeMockObject(mock);
initAction?.Invoke(mockObject);
return mockObject;
}
@@ -1117,15 +1080,26 @@ public Mock GetProtectedMock(Type type, params object?[] args)
}
///
- /// Gets the required mock.
+ /// Gets the required mock that already exists. If it doesn't exist, an error is raised.
///
/// The mock type, usually an interface.
/// Mock.
- /// type must be a class. - type
- /// type must be a class. - type
- public Mock GetRequiredMock(Type type) => type == null || (!type.IsClass && !type.IsInterface)
- ? throw new ArgumentException("type must be a class.", nameof(type))
- : mockCollection.First(x => x.Type == type).Mock;
+ /// Type cannot be null.
+ /// Type must be a class.
+ /// Type must be a class. - type
+ /// Not Found.
+ public Mock GetRequiredMock(Type type)
+ {
+ ArgumentNullException.ThrowIfNull(type);
+
+ var mock = (!type.IsClass && !type.IsInterface)
+ ? throw new ArgumentException("Type must be a class.", nameof(type))
+ : mockCollection.First(x => x.Type == type).Mock;
+
+ mock.RaiseIfNull();
+
+ return mock;
+ }
///
/// Gets the required mock.
@@ -1361,6 +1335,21 @@ internal MockModel AddMock(Mock mock, Type type, bool overwrite = false, bool no
///
public void CallMethod(Delegate method, params object?[]? args) => CallMethod(method, args);
+ internal void AddProperty(object? obj, PropertyInfo writableProperty)
+ {
+ try
+ {
+ if (writableProperty.GetValue(obj) is null && !creatingTypeList.Contains(writableProperty.PropertyType))
+ {
+ writableProperty.SetValue(obj, GetObject(writableProperty.PropertyType));
+ }
+ }
+ catch (Exception ex)
+ {
+ ExceptionLog.Add(ex.Message);
+ }
+ }
+
internal List> CreateArgPairList(MethodBase info, params object?[]? args)
{
var paramList = info?.GetParameters().ToList() ?? new();
@@ -1617,19 +1606,23 @@ internal IInstanceModel GetTypeFromInterface() where T : class
internal IInstanceModel GetTypeModel(Type type) =>
typeMap.TryGetValue(type, out var model) && model is not null ? model : new InstanceModel(type, this.GetTypeFromInterface(type));
- private void AddProperty(object? obj, PropertyInfo writableProperty)
+ internal void SetupMock(Type type, Mock oMock)
{
- try
+ if (!Strict)
{
- if (writableProperty.GetValue(obj) is null && !creatingTypeList.Contains(writableProperty.PropertyType))
+ if (oMock.Setups.Count == 0)
{
- writableProperty.SetValue(obj, GetObject(writableProperty.PropertyType));
+ // Only run this if there are no setups.
+ InvokeMethod(null, SETUP_ALL_PROPERTIES_METHOD_NAME, true, oMock);
+ }
+
+ if (InnerMockResolution)
+ {
+ AddProperties(type, this.GetSafeMockObject(oMock));
}
}
- catch (Exception ex)
- {
- ExceptionLog.Add(ex.Message);
- }
+
+ AddInjections(this.GetSafeMockObject(oMock), GetTypeModel(type)?.InstanceType ?? type);
}
}
}
diff --git a/FastMoq.Tests/MockerCreationExtensionsTests.cs b/FastMoq.Tests/MockerCreationExtensionsTests.cs
new file mode 100644
index 0000000..8ee736a
--- /dev/null
+++ b/FastMoq.Tests/MockerCreationExtensionsTests.cs
@@ -0,0 +1,62 @@
+using Castle.Core.Logging;
+using FastMoq.Extensions;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc.Infrastructure;
+
+namespace FastMoq.Tests
+{
+ public class MockerCreationExtensionsTests
+ {
+ private Mocker Mocks { get; } = new Mocker();
+
+ [Fact]
+ public void CreateMock_IActionContextAccessor_ShouldCreateMock()
+ {
+ var o = Mocks.CreateMockInternal();
+ o.Should().NotBeNull();
+ o.Should().BeOfType>();
+ o.Object.Should().NotBeNull();
+
+ var p = Mocks.CreateMockInternal();
+ p.Should().NotBeNull();
+ p.Should().BeOfType>();
+ p.Object.Should().NotBeNull();
+
+ var q = Mocks.CreateMockInternal();
+ q.Should().NotBeNull();
+ q.Should().BeOfType>();
+ q.Object.Should().NotBeNull();
+
+ var r = Mocks.CreateMockInternal();
+ r.Should().NotBeNull();
+ r.Should().BeOfType>();
+ r.Object.Should().NotBeNull();
+ var rObj = r.Object;
+ rObj.Session.Should().NotBeNull();
+ rObj.Items.Should().NotBeNull();
+ rObj.User.Should().NotBeNull();
+ rObj.Response.Should().BeNull();
+ }
+
+ [Fact]
+ public void SetupMockPropertyByPropertyInfo()
+ {
+ var mock = Mocks.GetMock();
+ mock.SetupMockProperty(typeof(IFormFile).GetProperty("Headers"), new HeaderDictionary());
+ }
+
+ [Fact]
+ public void SetupMockPropertyByName()
+ {
+ var mock = Mocks.GetMock();
+ mock.SetupMockProperty("Headers", new HeaderDictionary());
+ }
+
+ [Fact]
+ public void SetupMockPropertyByExpression()
+ {
+ var mock = Mocks.GetMock();
+ mock.SetupMockProperty(x=>x.Headers, new HeaderDictionary());
+ }
+ }
+}
diff --git a/FastMoq.Web/FastMoq.Web.csproj b/FastMoq.Web/FastMoq.Web.csproj
index 5427a4f..2ec349d 100644
--- a/FastMoq.Web/FastMoq.Web.csproj
+++ b/FastMoq.Web/FastMoq.Web.csproj
@@ -42,9 +42,11 @@
+
+
+
-