diff --git a/FastMoq.Core/Mocker.cs b/FastMoq.Core/Mocker.cs
index 22e054e..e1d5a6d 100644
--- a/FastMoq.Core/Mocker.cs
+++ b/FastMoq.Core/Mocker.cs
@@ -3,8 +3,10 @@
using Microsoft.Data.Sqlite;
using Microsoft.EntityFrameworkCore;
using Moq;
+using Moq.Language.Flow;
using Moq.Protected;
using System.Collections;
+using System.Collections.ObjectModel;
using System.Data.Common;
using System.Diagnostics.CodeAnalysis;
using System.IO.Abstractions;
@@ -32,21 +34,26 @@ public class Mocker
public readonly MockFileSystem fileSystem;
///
- /// List of .
+ /// The list of types in the process of being created. This is used to prevent circular creations.
///
- protected internal readonly List mockCollection;
+ protected internal readonly List creatingTypeList = new();
///
- /// The list of types in the process of being created. This is used to prevent circular creations.
+ /// List of .
///
- protected internal readonly List creatingTypeList = new List();
+ protected internal readonly List mockCollection;
///
/// of mapped to .
- /// This map assists in resolution of interfaces to instances.
+ /// This map assists in resolution of interfaces to instances.
///
/// The type map.
- internal readonly Dictionary typeMap;
+ internal readonly Dictionary typeMap;
+
+ ///
+ /// The constructor history
+ ///
+ private readonly Dictionary> constructorHistory = new();
///
/// The setup HTTP factory
@@ -57,6 +64,13 @@ public class Mocker
#region Properties
+ ///
+ /// Gets the constructor history.
+ ///
+ /// The constructor history.
+ public ILookup> ConstructorHistory =>
+ constructorHistory.ToLookup(pair => pair.Key, pair => pair.Value.AsReadOnly());
+
///
/// Gets the database connection.
///
@@ -78,14 +92,18 @@ public class Mocker
///
/// Gets or sets a value indicating whether this is strict.
///
- /// true if strict resolution; otherwise, false uses the built-in virtual
- /// .
- /// If strict, the mock
- /// does
- /// not use and uses of .
- /// Gets or sets a value indicating whether this is strict. If strict, the mock
- /// does
- /// not use the pre-built HttpClient and uses of .
+ ///
+ /// true if strict resolution; otherwise, false uses the built-in virtual
+ /// .
+ ///
+ ///
+ /// If strict, the mock
+ /// does
+ /// not use and uses of .
+ /// Gets or sets a value indicating whether this is strict. If strict, the mock
+ /// does
+ /// not use the pre-built HttpClient and uses of .
+ ///
public bool Strict { get; set; }
#endregion
@@ -107,7 +125,7 @@ public Mocker()
/// The typeMap assists in resolution of interfaces to instances.
///
/// The type map.
- public Mocker(Dictionary typeMap) : this() => this.typeMap = typeMap;
+ public Mocker(Dictionary typeMap) : this() => this.typeMap = typeMap;
///
/// Adds the injections to the specified object properties and fields.
@@ -135,17 +153,70 @@ public T AddInjections(T obj, Type? referenceType = null) where T : class?
///
/// Creates a with the given with the option of overwriting an existing
- ///
+ ///
///
/// The Mock , usually an interface.
/// Mock to Add.
- /// Overwrite if the mock exists or throw if this parameter is
- /// false.
+ ///
+ /// Overwrite if the mock exists or throw if this parameter is
+ /// false.
+ ///
/// if set to true uses public and non public constructors.
/// .
public MockModel AddMock(Mock mock, bool overwrite, bool nonPublic = false) where T : class =>
new(AddMock(mock, typeof(T), overwrite, nonPublic));
+ ///
+ /// Adds the property data to the object.
+ ///
+ ///
+ /// The object.
+ /// T.
+ public T? AddProperties(T obj)
+ {
+ var o = AddProperties(typeof(T), obj);
+ return o is not null ? (T) o : default;
+ }
+
+ ///
+ /// Adds the property data to the object.
+ ///
+ /// The type.
+ /// The object.
+ /// object.
+ public object? AddProperties(Type type, object? obj)
+ {
+ if (creatingTypeList.Contains(type))
+ {
+ return obj;
+ }
+
+ try
+ {
+ creatingTypeList.Add(type);
+ var writableProperties = type.GetProperties().Where(x => x.CanWrite && x.CanRead).ToList();
+ foreach (var writableProperty in writableProperties)
+ {
+ try
+ {
+ if (writableProperty.GetValue(obj) is null && !creatingTypeList.Contains(writableProperty.PropertyType))
+ {
+ writableProperty.SetValue(obj, GetObject(writableProperty.PropertyType));
+ }
+ }
+ catch
+ {
+ // Continue
+ }
+ }
+ }
+ finally
+ {
+ creatingTypeList.Remove(type);
+ }
+
+ return obj;
+ }
///
/// Adds an interface to Class mapping to the for easier resolution.
@@ -154,9 +225,10 @@ public MockModel AddMock(Mock mock, bool overwrite, bool nonPublic = fa
/// The Class Type (cannot be an interface) that can be created and assigned to tInterface.
/// An optional create function used to create the class.
/// Replace type if already exists. Default: false.
+ /// arguments needed in model.
/// $"{tClass.Name} cannot be an interface.
/// $"{tClass.Name} is not assignable to {tInterface.Name}.
- public void AddType(Type tInterface, Type tClass, Func? createFunc = null, bool replace = false)
+ public void AddType(Type tInterface, Type tClass, Func? createFunc = null, bool replace = false, params object?[]? args)
{
if (tClass.IsInterface)
{
@@ -173,7 +245,7 @@ public void AddType(Type tInterface, Type tClass, Func? createFu
typeMap.Remove(tInterface);
}
- typeMap.Add(tInterface, new InstanceModel(tClass, createFunc));
+ typeMap.Add(tInterface, new InstanceModel(tInterface, tClass, createFunc, args?.ToList() ?? new()));
}
///
@@ -182,7 +254,9 @@ public void AddType(Type tInterface, Type tClass, Func? createFu
///
/// The create function.
/// if set to true [replace].
- public void AddType(Func? createFunc = null, bool replace = false) where T : class => AddType(createFunc, replace);
+ /// arguments needed in model.
+ public void AddType(Func? createFunc = null, bool replace = false, params object?[]? args) where T : class =>
+ AddType(createFunc, replace, args);
///
/// Adds an interface to Class mapping to the for easier resolution.
@@ -191,10 +265,11 @@ public void AddType(Type tInterface, Type tClass, Func? createFu
/// The Class Type (cannot be an interface) that can be created and assigned to TInterface />.
/// An optional create function used to create the class.
/// Replace type if already exists. Default: false.
+ /// arguments needed in model.
/// $"{typeof(TClass).Name} cannot be an interface."
/// $"{typeof(TClass).Name} is not assignable to {typeof(TInterface).Name}."
- public void AddType(Func? createFunc = null, bool replace = false)
- where TInterface : class where TClass : class => AddType(typeof(TInterface), typeof(TClass), createFunc, replace);
+ public void AddType(Func? createFunc = null, bool replace = false, params object?[]? args)
+ where TInterface : class where TClass : class => AddType(typeof(TInterface), typeof(TClass), createFunc, replace, args);
///
/// Determines whether this instance contains a Mock of T .
@@ -212,10 +287,14 @@ public void AddType(Func? createFunc = null,
/// true if exists; otherwise, false .
/// type
/// type must be a class. - type
- public bool Contains(Type type) =>
- type == null ? throw new ArgumentNullException(nameof(type)) :
- !type.IsClass && !type.IsInterface ? throw new ArgumentException("type must be a class.", nameof(type)) :
- mockCollection.Any(x => x.Type == type);
+ public bool Contains(Type type)
+ {
+ ArgumentNullException.ThrowIfNull(type);
+
+ return !type.IsClass && !type.IsInterface
+ ? throw new ArgumentException("type must be a class.", nameof(type))
+ : mockCollection.Any(x => x.Type == type);
+ }
///
/// Creates the HTTP client.
@@ -233,10 +312,10 @@ public HttpClient CreateHttpClient(string clientName = "FastMoqHttpClient", stri
if (!Contains())
{
SetupHttpMessage(() => new HttpResponseMessage
- {
- StatusCode = statusCode,
- Content = new StringContent(stringContent)
- }
+ {
+ StatusCode = statusCode,
+ Content = new StringContent(stringContent),
+ }
);
}
@@ -253,13 +332,13 @@ public HttpClient CreateHttpClient(string clientName = "FastMoqHttpClient", stri
///
/// Creates an instance of T . Parameters allow matching of constructors and using those values in the creation
- /// of the instance.
+ /// of the instance.
///
/// The Mock , usually an interface.
/// The optional arguments used to create the instance.
/// .
///
- /// ();
/// ]]>
///
@@ -459,11 +538,24 @@ public HttpClient CreateHttpClient(string clientName = "FastMoqHttpClient", stri
}
var tType = typeof(T);
- var typeInstanceModel = GetMapModel() ?? (tType.IsInterface ? GetTypeFromInterface() : new InstanceModel());
+ var typeInstanceModel = GetTypeModel();
- if (typeInstanceModel.CreateFunc != null)
+ if (typeInstanceModel.CreateFunc != null && !creatingTypeList.Contains(tType))
{
- return (T)typeInstanceModel.CreateFunc.Invoke(this);
+ creatingTypeList.Add(tType);
+ T obj;
+
+ try
+ {
+ AddToConstructorHistory(tType, typeInstanceModel);
+ obj = (T) typeInstanceModel.CreateFunc.Invoke(this);
+ }
+ finally
+ {
+ creatingTypeList.Remove(tType);
+ }
+
+ return obj;
}
args ??= Array.Empty();
@@ -478,14 +570,16 @@ public HttpClient CreateHttpClient(string clientName = "FastMoqHttpClient", stri
///
/// Creates an instance of T .
- /// Non public constructors are included as options for creating the instance.
- /// Parameters allow matching of constructors and using those values in the creation of the instance.
+ /// Non public constructors are included as options for creating the instance.
+ /// Parameters allow matching of constructors and using those values in the creation of the instance.
///
/// The Mock , usually an interface.
/// The arguments.
- ///
+ ///
+ ///
+ ///
///
- /// ();
/// ]]>
///
@@ -494,7 +588,7 @@ public HttpClient CreateHttpClient(string clientName = "FastMoqHttpClient", stri
var type = typeof(T).IsInterface ? GetTypeFromInterface() : new InstanceModel();
return type.CreateFunc != null
- ? (T)type.CreateFunc.Invoke(this)
+ ? (T) type.CreateFunc.Invoke(this)
: CreateInstanceNonPublic(type.InstanceType, args) as T;
}
@@ -568,7 +662,6 @@ public Mock CreateMockInstance(Type type, bool nonPublic = false, params object?
throw new ArgumentException("type must be a class or interface.", nameof(type));
}
-
var constructor = new ConstructorModel(null, args.ToList());
try
@@ -596,80 +689,11 @@ public Mock CreateMockInstance(Type type, bool nonPublic = false, params object?
}
}
- AddInjections(oMock.Object, GetMapModel(type)?.InstanceType ?? type);
+ AddInjections(oMock.Object, GetTypeModel(type)?.InstanceType ?? type);
return oMock;
}
- ///
- /// Creates the mock internal.
- ///
- /// The type to create.
- /// The constructor model.
- /// Mock.
- private Mock CreateMockInternal(Type type, ConstructorModel constructor)
- {
- var newType = typeof(Mock<>).MakeGenericType(type);
-
- // Execute new Mock with Loose Behavior and arguments from constructor, if applicable.
- var parameters = new List { Strict ? MockBehavior.Strict : MockBehavior.Loose };
- constructor?.ParameterList.ToList().ForEach(parameters.Add);
-
- return Activator.CreateInstance(newType, parameters.ToArray()) is not Mock oMock ? throw new ApplicationException("Cannot create instance.") : oMock;
- }
-
- ///
- /// Adds the property data to the object.
- ///
- ///
- /// The object.
- /// T.
- public T? AddProperties(T obj)
- {
- var o = AddProperties(typeof(T), obj);
- return o is not null ? (T)o : default;
- }
-
- ///
- /// Adds the property data to the object.
- ///
- /// The type.
- /// The object.
- /// object.
- public object? AddProperties(Type type, object? obj)
- {
- if (creatingTypeList.Contains(type))
- {
- return obj;
- }
-
- try
- {
- creatingTypeList.Add(type);
- var writableProperties = type.GetProperties().Where(x => x.CanWrite && x.CanRead).ToList();
- foreach (var writableProperty in writableProperties)
- {
- try
- {
- if (writableProperty.GetValue(obj) is null && !creatingTypeList.Contains(writableProperty.PropertyType))
- {
- writableProperty.SetValue(obj, GetObject(writableProperty.PropertyType));
- }
- }
- catch
- {
- // Continue
- }
- }
- }
- finally
- {
- creatingTypeList.Remove(type);
- }
-
- return obj;
- }
-
///
/// Creates the mock instance that is not automatically injected.
///
@@ -679,7 +703,8 @@ private Mock CreateMockInternal(Type type, ConstructorModel constructor)
/// Mock.
/// type must be a class. - type
/// Cannot create instance.
- public Mock CreateMockInstance(bool nonPublic = false, params object?[] args) where T : class => (Mock)CreateMockInstance(typeof(T), nonPublic, args);
+ public Mock CreateMockInstance(bool nonPublic = false, params object?[] args) where T : class =>
+ (Mock) CreateMockInstance(typeof(T), nonPublic, args);
///
/// Gets the argument data.
@@ -712,6 +737,34 @@ public async Task GetContentBytes(HttpContent content) =>
public async Task GetContentStream(HttpContent content) =>
content is ByteArrayContent data ? await data.ReadAsStreamAsync() : Stream.Null;
+ ///
+ /// Gets the database context.
+ ///
+ /// The type of the t context.
+ /// TContext.
+ public TContext GetDbContext() where TContext : DbContext, new() => GetDbContext(_ => new TContext());
+
+ ///
+ /// Gets the database context.
+ ///
+ /// The type of the t context.
+ /// The new object function.
+ /// TContext.
+ public TContext GetDbContext(Func newObjectFunc) where TContext : DbContext
+ {
+ DbConnection = new SqliteConnection("DataSource=:memory:");
+ DbConnection.Open();
+ var dbContextOptions = new DbContextOptionsBuilder()
+ .UseSqlite(DbConnection)
+ .Options;
+
+ var context = newObjectFunc(dbContextOptions);
+ context.Database.EnsureCreated();
+ context.SaveChanges();
+
+ return context;
+ }
+
///
/// Gets the default value.
///
@@ -723,9 +776,20 @@ public async Task GetContentStream(HttpContent content) =>
{ FullName: "System.String" } => string.Empty,
_ when typeof(IEnumerable).IsAssignableFrom(type) => Array.CreateInstance(type.GetElementType() ?? typeof(object), 0),
{ IsClass: true } => null,
- _ => Activator.CreateInstance(type)
+ _ => Activator.CreateInstance(type),
};
+ ///
+ /// Gets the HTTP handler setup.
+ ///
+ /// The request.
+ /// The cancellation token.
+ /// ISetup<HttpMessageHandler, Task<HttpResponseMessage>>.
+ public ISetup>? GetHttpHandlerSetup(Expression? request = null,
+ Expression? cancellationToken = null) =>
+ GetMessageProtectedAsync("SendAsync", request ?? ItExpr.IsAny(),
+ cancellationToken ?? ItExpr.IsAny());
+
///
/// Gets a list with the specified number of list items, using a custom function.
///
@@ -735,14 +799,15 @@ _ when typeof(IEnumerable).IsAssignableFrom(type) => Array.CreateInstance(type.G
/// The initialize action.
/// .
///
- /// Example of how to create a list.
- /// (3, (i) => new Model(name: i.ToString()));
/// ]]>
- /// or
- /// (3, (i) => Mocks.CreateInstance(i));
- /// ]]>
+ /// ]]>
+ ///
public static List GetList(int count, Func? func, Action? initAction)
{
var results = new List();
@@ -768,14 +833,15 @@ public static List GetList(int count, Func? func, Action?
/// The function for creating the list items.
/// .
///
- /// Example of how to create a list.
- /// (3, (i) => new Model(name: i.ToString()));
/// ]]>
- /// or
- /// (3, (i) => Mocks.CreateInstance(i));
- /// ]]>
+ /// ]]>
+ ///
public static List GetList(int count, Func? func) => GetList(count, func, null);
///
@@ -786,17 +852,31 @@ public static List GetList(int count, Func? func, Action?
/// The function for creating the list items.
/// .
///
- /// Example of how to create a list.
- /// (3, () => new Model(name: Guid.NewGuid().ToString()));
/// ]]>
- /// or
- /// (3, () => Mocks.CreateInstance());
- /// ]]>
+ /// ]]>
+ ///
public static List GetList(int count, Func? func) =>
func == null ? new List() : GetList(count, _ => func.Invoke());
+ ///
+ /// Gets the message protected asynchronous.
+ ///
+ /// The type of the t mock.
+ /// The type of the t return.
+ /// Name of the method or property.
+ /// The arguments.
+ /// ISetup<TMock, Task<TReturn>>.
+ public ISetup>? GetMessageProtectedAsync(string methodOrPropertyName, params object?[]? args)
+ where TMock : class =>
+ GetMock().Protected()
+ ?.Setup>(methodOrPropertyName, args ?? Array.Empty());
+
///
/// Gets the method argument data.
///
@@ -850,7 +930,7 @@ public static List GetList(int count, Func? func) =>
/// The Mock , usually an interface.
/// The arguments to get the constructor.
/// .
- public Mock GetMock(params object?[] args) where T : class => (Mock)GetMock(typeof(T), args);
+ public Mock GetMock(params object?[] args) where T : class => (Mock) GetMock(typeof(T), args);
///
/// Gets of creates the mock of type .
@@ -874,7 +954,9 @@ public Mock GetMock(Type type, params object?[] args)
/// Gets the instance for the given .
///
/// The .
- ///
+ ///
+ ///
+ ///
/// nameof(info)
/// nameof(info)
/// nameof(info)
@@ -895,13 +977,6 @@ public Mock GetMock(Type type, params object?[] args)
}
}
- ///
- /// Ensure Type is correct.
- ///
- /// The type.
- /// Type.
- internal Type CleanType(Type type) => (type.Name.EndsWith('&')) ? type.Assembly.GetTypes().FirstOrDefault(x => x.Name.Equals(type.Name.TrimEnd('&'))) ?? type : type;
-
///
/// Gets the instance for the given type .
///
@@ -920,12 +995,12 @@ public Mock GetMock(Type type, params object?[] args)
type = CleanType(type);
- var typeValueModel = GetMapModel(type);
+ var typeValueModel = GetTypeModel(type);
- if (typeValueModel?.CreateFunc != null)
+ if (typeValueModel.CreateFunc != null)
{
// If a create function is provided, use it instead of a mock object.
- return AddInjections(typeValueModel.CreateFunc?.Invoke(this), typeValueModel.InstanceType);
+ return AddInjections(typeValueModel.CreateFunc.Invoke(this), typeValueModel.InstanceType);
}
if (!Strict)
@@ -1009,7 +1084,7 @@ public Mock GetRequiredMock(Type type) => type == null || (!type.IsClass && !typ
/// .
/// type must be a class. - type
/// Mock must exist. - type
- public Mock GetRequiredMock() where T : class => (Mock)GetRequiredMock(typeof(T));
+ public Mock GetRequiredMock() where T : class => (Mock) GetRequiredMock(typeof(T));
///
/// Gets the content of the string.
@@ -1024,17 +1099,22 @@ public async Task GetStringContent(HttpContent content) =>
///
/// The Mock , usually an interface.
/// The action.
- /// False to keep the existing setup.
- ///
+ ///
+ /// False to keep the existing setup.
+ ///
+ ///
+ ///
+ ///
/// Invalid Mock.
///
- /// Example of how to set up for mocks that require specific functionality.
- /// (mock => {
/// mock.Setup(x => x.StartCar).Returns(true));
/// mock.Setup(x => x.StopCar).Returns(false));
/// }
- /// ]]>
+ /// ]]>
+ ///
public Mock Initialize(Action> action, bool reset = true) where T : class
{
var mock = GetMock() ?? throw new InvalidOperationException("Invalid Mock.");
@@ -1082,9 +1162,12 @@ public Mock Initialize(Action> action, bool reset = true) where T
var method = type.InstanceType.GetMethod(methodName, flags);
- return method == null && !nonPublic && !Strict ? InvokeMethod(obj, methodName, true, args) :
- method == null ? throw new ArgumentOutOfRangeException() :
- method.Invoke(obj, flags, null, args?.Any() ?? false ? args.ToArray() : GetMethodArgData(method), null);
+ return method switch
+ {
+ null when !nonPublic && !Strict => InvokeMethod(obj, methodName, true, args),
+ null => throw new ArgumentOutOfRangeException(),
+ _ => method.Invoke(obj, flags, null, args?.Any() ?? false ? args.ToArray() : GetMethodArgData(method), null),
+ };
}
///
@@ -1138,7 +1221,7 @@ public void SetupMessageAsync(Expression
(GetMock()
.Setup(expression) ?? throw new InvalidDataException($"Unable to setup '{typeof(TMock)}'."))
- .ReturnsAsync(messageFunc)?.Verifiable();
+ .ReturnsAsync(messageFunc)?.Verifiable();
///
/// Setups the message protected.
@@ -1165,16 +1248,18 @@ public void SetupMessageProtected(string methodOrPropertyName, F
public void SetupMessageProtectedAsync(string methodOrPropertyName, Func messageFunc, params object?[]? args)
where TMock : class =>
GetMock().Protected()
- ?.Setup>(methodOrPropertyName, args ?? Array.Empty())
- ?.ReturnsAsync(messageFunc)?.Verifiable();
+ ?.Setup>(methodOrPropertyName, args ?? Array.Empty())
+ ?.ReturnsAsync(messageFunc)?.Verifiable();
///
/// Add specified Mock. Internal API only.
///
/// Mock to Add.
/// Type of Mock.
- /// Overwrite if the mock exists or throw if this parameter is
- /// false.
+ ///
+ /// Overwrite if the mock exists or throw if this parameter is
+ /// false.
+ ///
/// if set to true [non public].
/// .
/// nameof(mock)
@@ -1210,6 +1295,51 @@ internal MockModel AddMock(Mock mock, Type type, bool overwrite = false, bool no
return GetMockModel(type);
}
+ ///
+ /// Adds to constructor history.
+ ///
+ /// The key.
+ /// The instance model.
+ /// bool.
+ internal bool AddToConstructorHistory(Type key, IHistoryModel instanceModel)
+ {
+ if (key is null || instanceModel is null)
+ {
+ return false;
+ }
+
+ var item = ConstructorHistory.FirstOrDefault(x => x.Key == key);
+ if (item?.Key is null)
+ {
+ constructorHistory.Add(key, new List { instanceModel });
+ }
+ else
+ {
+ constructorHistory[key].Add(instanceModel);
+ }
+
+ return true;
+ }
+
+ ///
+ /// Adds to constructor history.
+ ///
+ /// The key.
+ /// The constructor information.
+ /// The arguments.
+ /// bool.
+ internal bool AddToConstructorHistory(Type key, ConstructorInfo? constructorInfo, List args) =>
+ AddToConstructorHistory(key, new ConstructorModel(constructorInfo, args));
+
+ ///
+ /// Ensure Type is correct.
+ ///
+ /// The type.
+ /// Type.
+ internal Type CleanType(Type type) => type.Name.EndsWith('&')
+ ? type.Assembly.GetTypes().FirstOrDefault(x => x.Name.Equals(type.Name.TrimEnd('&'))) ?? type
+ : type;
+
///
/// Creates the HTTP client internal.
///
@@ -1218,7 +1348,7 @@ internal MockModel AddMock(Mock mock, Type type, bool overwrite = false, bool no
internal HttpClient CreateHttpClientInternal(Uri baseUri) =>
new(GetObject() ?? throw new ApplicationException("Unable to create HttpMessageHandler."))
{
- BaseAddress = baseUri
+ BaseAddress = baseUri,
};
///
@@ -1228,13 +1358,13 @@ internal HttpClient CreateHttpClientInternal(Uri baseUri) =>
/// The constructor function.
/// The arguments.
/// T.
- internal T? CreateInstanceInternal(Func constructorFunc, Dictionary? data) where T : class
+ internal T? CreateInstanceInternal(Func constructorFunc, Dictionary? data) where T : class
{
var type = typeof(T).IsInterface ? GetTypeFromInterface() : new InstanceModel();
if (type.CreateFunc != null)
{
- return (T)type.CreateFunc.Invoke(this);
+ return (T) type.CreateFunc.Invoke(this);
}
data ??= new Dictionary();
@@ -1273,6 +1403,7 @@ internal HttpClient CreateHttpClientInternal(Uri baseUri) =>
/// object?.
internal object? CreateInstanceInternal(Type type, ConstructorInfo? info, params object?[] args)
{
+ AddToConstructorHistory(type, info, args.ToList());
var paramList = info?.GetParameters().ToList() ?? new();
var newArgs = args.ToList();
@@ -1309,9 +1440,12 @@ internal ConstructorModel FindConstructor(Type type, bool nonPublic, params obje
)
.ToList();
- return !constructors.Any() && !nonPublic && !Strict ? FindConstructor(type, true, args) :
- !constructors.Any() ? throw new NotImplementedException("Unable to find the constructor.") :
- constructors.First();
+ return constructors.Any() switch
+ {
+ false when !nonPublic && !Strict => FindConstructor(type, true, args),
+ false => throw new NotImplementedException("Unable to find the constructor."),
+ _ => constructors[0],
+ };
}
///
@@ -1322,9 +1456,15 @@ internal ConstructorModel FindConstructor(Type type, bool nonPublic, params obje
/// if set to true [non public].
/// Constructors to ignore.
/// .
- /// Multiple parameterized constructors exist. Cannot decide which to use.
+ ///
+ /// Multiple parameterized constructors exist. Cannot decide which to
+ /// use.
+ ///
/// Unable to find the constructor.
- /// Multiple parameterized constructors exist. Cannot decide which to use.
+ ///
+ /// Multiple parameterized constructors exist. Cannot
+ /// decide which to use.
+ ///
/// Unable to find the constructor.
internal ConstructorModel FindConstructor(bool bestGuess, Type type, bool nonPublic, List? excludeList = null)
{
@@ -1357,48 +1497,6 @@ internal ConstructorModel FindConstructor(bool bestGuess, Type type, bool nonPub
return validConstructors.Last();
}
- ///
- /// Gets the tested constructors.
- ///
- /// The type to try to create.
- /// The constructors to test with the specified type.
- /// List<FastMoq.Models.ConstructorModel>.
- private List GetTestedConstructors(Type type, List constructors)
- {
- constructors ??= new();
- var validConstructors = new List();
-
- if (constructors.Count <= 1)
- {
- return constructors;
- }
-
- var targetError = new List();
-
- foreach (var constructor in constructors)
- {
- try
- {
- // Test Constructor.
- var mock = CreateMockInternal(type, constructor);
- _ = mock.Object;
- validConstructors.Add(constructor);
- }
- catch (TargetInvocationException)
- {
- // Track invocation issues to bubble up if a good constructor is not found.
- targetError.Add(constructor);
- }
- catch
- {
- // Ignore
- }
- }
-
- return validConstructors.Any() ? validConstructors : targetError;
-
- }
-
///
/// Finds the type of the constructor by.
///
@@ -1411,8 +1509,12 @@ internal ConstructorInfo FindConstructorByType(Type type, bool nonPublic, params
{
var constructors = GetConstructorsByType(nonPublic, type, args);
- return !constructors.Any() && !nonPublic && !Strict ? FindConstructorByType(type, true, args) :
- !constructors.Any() ? throw new NotImplementedException("Unable to find the constructor.") : constructors.First();
+ return constructors.Any() switch
+ {
+ false when !nonPublic && !Strict => FindConstructorByType(type, true, args),
+ false => throw new NotImplementedException("Unable to find the constructor."),
+ _ => constructors[0],
+ };
}
///
@@ -1494,37 +1596,6 @@ internal List GetConstructorsNonPublic(Type type,
.Select(x => new ConstructorModel(x)).ToList();
}
- ///
- /// Gets the database context.
- ///
- /// The type of the t context.
- /// TContext.
- public TContext GetDbContext() where TContext : DbContext, new()
- {
- return GetDbContext(_=> new TContext());
- }
-
- ///
- /// Gets the database context.
- ///
- /// The type of the t context.
- /// The new object function.
- /// TContext.
- public TContext GetDbContext(Func newObjectFunc) where TContext : DbContext
- {
- DbConnection = new SqliteConnection("DataSource=:memory:");
- DbConnection.Open();
- var dbContextOptions = new DbContextOptionsBuilder()
- .UseSqlite(DbConnection)
- .Options;
-
- var context = newObjectFunc(dbContextOptions);
- context.Database.EnsureCreated();
- context.SaveChanges();
-
- return context;
- }
-
///
/// Gets the injection fields.
///
@@ -1551,21 +1622,6 @@ internal static IEnumerable GetInjectionProperties(Type type, Type
y.AttributeType == attributeType ||
y.AttributeType.Name.Equals("InjectAttribute", StringComparison.OrdinalIgnoreCase)));
- ///
- /// Gets the map model.
- ///
- /// The type of the t model.
- /// FastMoq.Models.InstanceModel<TModel>?.
- internal InstanceModel? GetMapModel() where TModel : class => GetMapModel(typeof(TModel)) as InstanceModel;
-
- ///
- /// Gets the map model.
- ///
- /// The type.
- /// FastMoq.Models.InstanceModel?.
- internal InstanceModel? GetMapModel(Type type) => typeMap.ContainsKey(type) ? typeMap[type] : null;
-
-
///
/// Gets the mock model.
///
@@ -1574,10 +1630,21 @@ internal static IEnumerable GetInjectionProperties(Type type, Type
/// Create Mock if it doesn't exist.
/// .
///
- internal MockModel GetMockModel(Type type, Mock? mock = null, bool autoCreate = true) =>
- mockCollection.FirstOrDefault(x => x.Type == type && (x.Mock == mock || mock == null)) ??
- (mock == null ? autoCreate ? GetMockModel(type, GetMock(type), autoCreate) : throw new NotImplementedException() :
- autoCreate ? AddMock(mock, type) : throw new NotImplementedException());
+ internal MockModel GetMockModel(Type type, Mock? mock = null, bool autoCreate = true)
+ {
+ var first = mockCollection.FirstOrDefault(x => x.Type == type && (x.Mock == mock || mock == null));
+ if (first != null)
+ {
+ return first;
+ }
+
+ if (!autoCreate)
+ {
+ throw new NotImplementedException();
+ }
+
+ return mock == null ? GetMockModel(type, GetMock(type), autoCreate) : AddMock(mock, type);
+ }
///
/// Gets the mock model.
@@ -1604,63 +1671,91 @@ internal MockModel GetMockModel(Mock? mock = null, bool autoCreate = tr
/// object?.
internal object? GetParameter(Type parameterType)
{
- if (parameterType.IsClass || parameterType.IsInterface)
+ if (!parameterType.IsClass && !parameterType.IsInterface)
{
- var typeValueModel = GetMapModel(parameterType);
- if (typeValueModel?.CreateFunc != null)
- {
- return typeValueModel.CreateFunc.Invoke(this);
- }
+ return GetDefaultValue(parameterType);
+ }
- if (!parameterType.IsSealed)
- {
- return GetObject(parameterType);
- }
+ var typeValueModel = GetTypeModel(parameterType);
+ if (typeValueModel.CreateFunc != null)
+ {
+ return typeValueModel.CreateFunc.Invoke(this);
}
- return GetDefaultValue(parameterType);
+ return !parameterType.IsSealed ? GetObject(parameterType) : GetDefaultValue(parameterType);
}
///
/// Gets the type from interface.
///
- /// The Mock , usually an interface.
- /// InstanceModel.
- ///
- ///
- internal InstanceModel GetTypeFromInterface() where T : class
+ /// Type of the t.
+ /// Type.
+ ///
+ internal Type GetTypeFromInterface(Type tType)
{
- var tType = typeof(T);
-
if (!tType.IsInterface)
{
- return new InstanceModel();
+ return tType;
}
- var mappedType = typeMap.Where(x => x.Key == typeof(T)).Select(x => x.Value).FirstOrDefault();
+ var mappedType = typeMap.Where(x => x.Key == tType).Select(x => x.Value).FirstOrDefault();
if (mappedType != null)
{
- return mappedType;
+ return mappedType.InstanceType;
}
var types = tType.Assembly.GetTypes().ToList();
// Get interfaces that contain T.
- List interfaces = types.Where(type => type.IsInterface && type.GetInterfaces().Contains(tType)).ToList();
+ var interfaces = types.Where(type => type.IsInterface && type.GetInterfaces().Contains(tType)).ToList();
// Get Types that contain T, but are not interfaces.
- List possibleTypes = types.Where(type =>
+ var possibleTypes = types.Where(type =>
type.GetInterfaces().Contains(tType) &&
interfaces.All(iType => type != iType) &&
!interfaces.Any(iType => iType.IsAssignableFrom(type))
).ToList();
- return new InstanceModel(possibleTypes.Count > 1 ? throw new AmbiguousImplementationException() :
- !possibleTypes.Any() ? throw new NotImplementedException() : possibleTypes.First()
- );
+ if (possibleTypes.Count > 1)
+ {
+ throw new AmbiguousImplementationException();
+ }
+
+ return !possibleTypes.Any() ? tType : possibleTypes[0];
+ }
+
+ ///
+ /// Gets the type from interface.
+ ///
+ /// The Mock , usually an interface.
+ /// InstanceModel.
+ ///
+ ///
+ internal IInstanceModel GetTypeFromInterface() where T : class
+ {
+ var tType = typeof(T);
+ var newType = GetTypeFromInterface(tType);
+ var model = new InstanceModel(tType, newType) ?? throw new NotImplementedException();
+
+ return model;
}
+ ///
+ /// Gets the map model.
+ ///
+ /// The type of the t model.
+ /// FastMoq.Models.InstanceModel<TModel>?.
+ internal IInstanceModel GetTypeModel() where TModel : class => GetTypeModel(typeof(TModel)) ?? new InstanceModel();
+
+ ///
+ /// Gets the map model.
+ ///
+ /// The type.
+ /// FastMoq.Models.InstanceModel?.
+ internal IInstanceModel GetTypeModel(Type type) =>
+ typeMap.ContainsKey(type) ? typeMap[type] : new InstanceModel(type, GetTypeFromInterface(type));
+
///
/// Determines whether [is mock file system] [the specified use predefined file system].
///
@@ -1754,5 +1849,65 @@ internal static bool IsValidConstructorByType(ConstructorInfo info, params Type?
/// The type.
///
internal static void ThrowAlreadyExists(Type type) => throw new ArgumentException($"{type} already exists.");
+
+ ///
+ /// Creates the mock internal.
+ ///
+ /// The type to create.
+ /// The constructor model.
+ /// Mock.
+ private Mock CreateMockInternal(Type type, ConstructorModel constructor)
+ {
+ var newType = typeof(Mock<>).MakeGenericType(type);
+
+ // Execute new Mock with Loose Behavior and arguments from constructor, if applicable.
+ var parameters = new List { Strict ? MockBehavior.Strict : MockBehavior.Loose };
+ constructor?.ParameterList.ToList().ForEach(parameters.Add);
+
+ return Activator.CreateInstance(newType, parameters.ToArray()) is not Mock oMock
+ ? throw new ApplicationException("Cannot create instance.")
+ : oMock;
+ }
+
+ ///
+ /// Gets the tested constructors.
+ ///
+ /// The type to try to create.
+ /// The constructors to test with the specified type.
+ /// List<FastMoq.Models.ConstructorModel>.
+ private List GetTestedConstructors(Type type, List constructors)
+ {
+ constructors ??= new();
+ var validConstructors = new List();
+
+ if (constructors.Count <= 1)
+ {
+ return constructors;
+ }
+
+ var targetError = new List();
+
+ foreach (var constructor in constructors)
+ {
+ try
+ {
+ // Test Constructor.
+ var mock = CreateMockInternal(type, constructor);
+ _ = mock.Object;
+ validConstructors.Add(constructor);
+ }
+ catch (TargetInvocationException)
+ {
+ // Track invocation issues to bubble up if a good constructor is not found.
+ targetError.Add(constructor);
+ }
+ catch
+ {
+ // Ignore
+ }
+ }
+
+ return validConstructors.Any() ? validConstructors : targetError;
+ }
}
-}
+}
\ No newline at end of file
diff --git a/FastMoq.Core/Models/ConstructorModel.cs b/FastMoq.Core/Models/ConstructorModel.cs
index 4108a4a..6c4774b 100644
--- a/FastMoq.Core/Models/ConstructorModel.cs
+++ b/FastMoq.Core/Models/ConstructorModel.cs
@@ -5,7 +5,7 @@ namespace FastMoq.Models
///
/// Class ConstructorModel.
///
- internal class ConstructorModel
+ public class ConstructorModel : IHistoryModel
{
#region Properties
diff --git a/FastMoq.Core/Models/IHistoryModel.cs b/FastMoq.Core/Models/IHistoryModel.cs
new file mode 100644
index 0000000..15a10bd
--- /dev/null
+++ b/FastMoq.Core/Models/IHistoryModel.cs
@@ -0,0 +1,6 @@
+namespace FastMoq.Models
+{
+ public interface IHistoryModel
+ {
+ }
+}
diff --git a/FastMoq.Core/Models/IInstanceModel.cs b/FastMoq.Core/Models/IInstanceModel.cs
new file mode 100644
index 0000000..b4a15f4
--- /dev/null
+++ b/FastMoq.Core/Models/IInstanceModel.cs
@@ -0,0 +1,30 @@
+namespace FastMoq.Models
+{
+ ///
+ /// Interface IInstanceModel
+ ///
+ public interface IInstanceModel : IHistoryModel
+ {
+ #region Properties
+
+ ///
+ /// Gets the type.
+ ///
+ /// The type.
+ Type Type { get; }
+
+ ///
+ /// Gets the create function.
+ ///
+ /// The create function.
+ Func? CreateFunc { get; }
+
+ ///
+ /// Gets the type of the instance.
+ ///
+ /// The type of the instance.
+ Type InstanceType { get; }
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/FastMoq.Core/Models/InstanceModel.cs b/FastMoq.Core/Models/InstanceModel.cs
index fe75042..2169761 100644
--- a/FastMoq.Core/Models/InstanceModel.cs
+++ b/FastMoq.Core/Models/InstanceModel.cs
@@ -2,108 +2,68 @@
namespace FastMoq.Models
{
- ///
///
/// Class InstanceModel.
- /// Implements the
+ /// Implements the
///
- /// The type of the t class.
- ///
+ ///
+ ///
+ ///
[ExcludeFromCodeCoverage]
- public class InstanceModel : InstanceModel where TClass : class
+ public class InstanceModel : IInstanceModel
{
#region Properties
- ///
- /// Gets or sets the create function.
- ///
- /// The create function.
- public new Func? CreateFunc
- {
- get => (Func?)base.CreateFunc;
- set => base.CreateFunc = value;
- }
-
- #endregion
-
///
- ///
- /// Initializes a new instance of the class.
- ///
- public InstanceModel() : this(null) { }
+ public virtual Type Type { get; }
///
- ///
- /// Initializes a new instance of the class.
- ///
- /// The create function.
- public InstanceModel(Func? createFunc) : base(typeof(TClass), createFunc) { }
+ public virtual Type InstanceType { get; }
///
- ///
- /// Initializes a new instance of the class.
- ///
- /// The create function.
- /// The arguments.
- public InstanceModel(Func? createFunc, List arguments) : this(createFunc) =>
- Arguments = arguments;
- }
-
- ///
- /// Class InstanceModel.
- /// Implements the
- ///
- ///
- [ExcludeFromCodeCoverage]
- public class InstanceModel
- {
- #region Properties
-
- ///
- /// Gets or sets the type of the instance.
- ///
- /// The type of the instance.
- public Type InstanceType { get; }
-
- ///
- /// Gets or sets the create function.
- ///
- /// The create function.
public Func? CreateFunc { get; internal set; }
///
/// Gets the arguments.
///
/// The arguments.
- public List Arguments { get; internal set; } = new();
+ public List Arguments { get; internal set; } = new();
#endregion
///
/// Initializes a new instance of the class.
///
+ /// Type of the original.
/// Type of the instance.
/// instanceType
- internal InstanceModel(Type instanceType) =>
+ internal InstanceModel(Type originalType, Type instanceType)
+ {
+ Type = originalType;
InstanceType = instanceType ?? throw new ArgumentNullException(nameof(instanceType));
+ }
- ///
///
- /// Initializes a new instance of the class.
+ /// Initializes a new instance of the class.
///
+ /// Type of the original.
/// Type of the instance.
- ///
- /// arguments
- internal InstanceModel(Type instanceType, Func? createFunc) : this(instanceType) => CreateFunc = createFunc;
-
+ /// The create function.
///
+ internal InstanceModel(Type originalType, Type instanceType, Func? createFunc) : this(originalType, instanceType)
+ => CreateFunc = createFunc;
+
///
- /// Initializes a new instance of the class.
+ /// Initializes a new instance of the class.
///
+ /// Type of the original.
/// Type of the instance.
- ///
+ /// The create function.
/// The arguments.
- /// arguments
- internal InstanceModel(Type instanceType, Func? createFunc, List arguments) : this(instanceType, createFunc) => Arguments = arguments ?? throw new ArgumentNullException(nameof(arguments));
+ /// arguments
+ ///
+ internal InstanceModel(Type originalType, Type instanceType, Func? createFunc, List arguments)
+ : this(originalType, instanceType, createFunc) =>
+ Arguments = arguments ?? throw new ArgumentNullException(nameof(arguments));
}
}
\ No newline at end of file
diff --git a/FastMoq.Core/Models/InstanceModelT.cs b/FastMoq.Core/Models/InstanceModelT.cs
new file mode 100644
index 0000000..a7282d6
--- /dev/null
+++ b/FastMoq.Core/Models/InstanceModelT.cs
@@ -0,0 +1,61 @@
+using System.Diagnostics.CodeAnalysis;
+
+namespace FastMoq.Models
+{
+ ///
+ ///
+ /// Class InstanceModel.
+ /// Implements the
+ ///
+ /// The type of the t class.
+ ///
+ [ExcludeFromCodeCoverage]
+ public class InstanceModel : InstanceModel where TClass : class
+ {
+ #region Properties
+
+ ///
+ public override Type InstanceType => typeof(TClass);
+
+ ///
+ /// Gets or sets the create function.
+ ///
+ /// The create function.
+ public new Func? CreateFunc
+ {
+ get => (Func?) base.CreateFunc;
+ set => base.CreateFunc = value;
+ }
+
+ #endregion
+
+ ///
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public InstanceModel() : this(default(Func)) { }
+
+ ///
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The create function.
+ public InstanceModel(Func? createFunc) : base(typeof(TClass), typeof(TClass), createFunc) { }
+
+ ///
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The create function.
+ /// The arguments.
+ public InstanceModel(Func? createFunc, List arguments) : this(createFunc) =>
+ Arguments = arguments;
+
+ ///
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The model.
+ public InstanceModel(InstanceModel model) : this(model.CreateFunc as Func, model.Arguments) { }
+ }
+}
diff --git a/FastMoq.Tests/InstanceModelTests.cs b/FastMoq.Tests/InstanceModelTests.cs
index 243f6d4..2f8a594 100644
--- a/FastMoq.Tests/InstanceModelTests.cs
+++ b/FastMoq.Tests/InstanceModelTests.cs
@@ -28,10 +28,10 @@ public void Create()
[Fact]
public void CreateNullType()
{
- Action a = () => _ = new InstanceModel(null) { CreateFunc = _ => new FileSystem() };
+ Action a = () => _ = new InstanceModel(null, null) { CreateFunc = _ => new FileSystem() };
a.Should().Throw();
- var im = new InstanceModel(null);
+ var im = new InstanceModel(default(Func));
im.InstanceType.Should().Be(typeof(IFileSystem));
im.CreateFunc.Should().BeNull();
}
@@ -39,12 +39,12 @@ public void CreateNullType()
[Fact]
public void CreateInstance()
{
- var obj = new InstanceModel(typeof(IFileSystem), mocker => new FileSystem(), new List());
+ var obj = new InstanceModel(typeof(IFileSystem), typeof(FileSystem), mocker => new FileSystem(), new List());
obj.Should().NotBeNull();
obj.CreateFunc.Should().NotBeNull();
obj.Arguments.Should().HaveCount(0);
- new Action(() => new InstanceModel(typeof(IFileSystem), mocker => new FileSystem(), null)).Should().Throw();
+ new Action(() => new InstanceModel(typeof(IFileSystem), typeof(FileSystem), mocker => new FileSystem(), null)).Should().Throw();
}
}
}
\ No newline at end of file
diff --git a/FastMoq.Tests/MocksTests.cs b/FastMoq.Tests/MocksTests.cs
index 87a8eeb..44c7444 100644
--- a/FastMoq.Tests/MocksTests.cs
+++ b/FastMoq.Tests/MocksTests.cs
@@ -157,6 +157,14 @@ public void Create_WithMapTest1()
o.Should().BeOfType();
}
+ [Fact]
+ public void Create_WithMapTest1b()
+ {
+ Mocks.AddType();
+ var o = Mocks.CreateInstance();
+ o.Should().BeOfType();
+ }
+
[Fact]
public void Create_WithMapTest2()
{
@@ -365,10 +373,14 @@ public void AddTypeT_ShouldBe_AddType()
{
Mocks.AddType(_ => Mocks.CreateInstance());
var t = Mocks.typeMap.First().Value.CreateFunc.Invoke(null);
+ var o = Mocks.CreateInstance();
+ o.Should().BeEquivalentTo(t);
Mocks.typeMap.Clear();
Mocks.AddType(typeof(TestClassOne), typeof(TestClassOne), _=> Mocks.CreateInstance());
var t2 = Mocks.typeMap.First().Value.CreateFunc.Invoke(null);
t.Should().BeEquivalentTo(t2);
+ o = Mocks.CreateInstance();
+ o.Should().BeEquivalentTo(t2);
}
[Fact]
@@ -605,11 +617,19 @@ public void GetObject_InitAction()
{
var obj = Component.GetObject(t => t.field2 = 3);
obj.field2.Should().Be(3);
-
+ Component.AddType();
var obj2 = Component.GetObject(t => t.Value = 333.333);
obj2.Value.Should().Be(333.333);
}
+ [Fact]
+ public void GetObject_InitAction_ShouldThrowWithoutMap()
+ {
+ var obj = Component.GetObject(t => t.field2 = 3);
+ obj.field2.Should().Be(3);
+ new Action (() => Component.GetObject(t => t.Value = 333.333)).Should().Throw();
+ }
+
[Fact]
public void GetObject_ShouldThrow()
{
@@ -724,14 +744,14 @@ public void Mocker_CreateMockInstanceNull_ShouldThrow() =>
[Fact]
public void Mocker_CreateWithEmptyMap()
{
- var test = new Mocker(new Dictionary());
+ var test = new Mocker(new Dictionary());
test.typeMap.Should().BeEmpty();
}
[Fact]
public void Mocker_CreateWithMap()
{
- var map = new Dictionary
+ var map = new Dictionary
{
{typeof(IFileSystem), new InstanceModel()},
{typeof(IFile), new InstanceModel(_ => new MockFileSystem().File)}