Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
#9: Redoes the Extension-Method-Approach
because this approach wasnt optimal. The current approach has much less redundant code. The only sad part is Moq, because it's _special_ with it's Mock<T> class
- Loading branch information
Showing
23 changed files
with
438 additions
and
744 deletions.
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,191 @@ | ||
using ArrangeContext.Core.Helper; | ||
using ArrangeContext.Core.Helper.Contracts; | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Reflection; | ||
|
||
namespace ArrangeContext.Core | ||
{ | ||
/// <summary> | ||
/// The base-class for everything that is ArrangeContext | ||
/// </summary> | ||
/// <typeparam name="TContext">The Context that is worked with</typeparam> | ||
public abstract class ArrangeContextBase<TContext> where TContext : class | ||
{ | ||
private readonly IReflectionHelper _reflectionHelper; | ||
|
||
private readonly IList<ContextParameter> _contextParameters; | ||
|
||
private readonly bool _includeOptionalParameters; | ||
private bool _initialized = false; | ||
|
||
/// <summary> | ||
/// Creates a new instance of the Arrange-Context | ||
/// </summary> | ||
/// <param name="includeOptionalParameters">If optional parameters should be considered or not</param> | ||
protected ArrangeContextBase(bool includeOptionalParameters) | ||
{ | ||
_includeOptionalParameters = includeOptionalParameters; | ||
|
||
_contextParameters = new List<ContextParameter>(); | ||
_reflectionHelper = new ReflectionHelper(); | ||
} | ||
|
||
/// <summary> | ||
/// Builds the Context with substituted ctor-Parameters | ||
/// </summary> | ||
/// <returns>A new instance of <typeparamref name="TContext"/> with substituted ctor-Parameters</returns> | ||
public TContext Build() | ||
{ | ||
ConsiderInitialization(); | ||
|
||
var parameterArray = _contextParameters | ||
.Select(p => p.Instance.Instance) | ||
.ToArray(); | ||
var instance = Activator.CreateInstance(typeof(TContext), parameterArray); | ||
return (TContext)instance; | ||
} | ||
|
||
/// <summary> | ||
/// Gets a <see cref="ContextParameter"/> of the given Type <typeparamref name="T"/> | ||
/// </summary> | ||
/// <typeparam name="T">The type of the parameter to find</typeparam> | ||
/// <exception cref="ArgumentException">When the parameter for the type <typeparamref name="T"/> could not be found</exception> | ||
/// <returns>The found parameter</returns> | ||
protected ContextParameter GetParameter<T>() | ||
{ | ||
ConsiderInitialization(); | ||
|
||
var parameter = _contextParameters.FirstOrDefault(p => p.Type == typeof(T)); | ||
if (parameter == null) | ||
throw new ArgumentException( | ||
$"The specified Type '{typeof(T).Name}' is not a constructor parameter of '{typeof(TContext).Name}'"); | ||
return parameter; | ||
} | ||
|
||
/// <summary> | ||
/// Gets a <see cref="ContextParameter"/> with the given <paramref name="parameterName"/> | ||
/// </summary> | ||
/// <exception cref="ArgumentException">When the parameter for the <paramref name="parameterName"/> could not be found</exception> | ||
/// <returns>The found parameter</returns> | ||
protected ContextParameter GetParameter(string parameterName) | ||
{ | ||
ConsiderInitialization(); | ||
|
||
var parameter = _contextParameters.FirstOrDefault(p => | ||
p.Name.Equals(parameterName, StringComparison.InvariantCultureIgnoreCase)); | ||
if (parameter == null) | ||
throw new ArgumentException( | ||
$"The specified parameter with name '{parameterName}' is not a constructor parameter of '{typeof(TContext).Name}'"); | ||
return parameter; | ||
} | ||
|
||
/// <summary> | ||
/// Replaces the <paramref name="parameterToReplace"/> with another provided instance | ||
/// </summary> | ||
/// <param name="parameterToReplace">The parameter to replace</param> | ||
/// <param name="instance">The instance to replace the current parameter with</param> | ||
/// <param name="mockedInstance">The mocked instance of the provided instance (i.e. for Mock<Something>)</param> | ||
protected void ReplaceInstance( | ||
ContextParameter parameterToReplace, | ||
object instance, | ||
object mockedInstance) | ||
{ | ||
ConsiderInitialization(); | ||
|
||
var index = _contextParameters.IndexOf(parameterToReplace); | ||
_contextParameters.Remove(parameterToReplace); | ||
|
||
var newParameter = new ContextParameter( | ||
parameterToReplace.Type, | ||
parameterToReplace.Name, | ||
new ContextInstance(instance, mockedInstance)); | ||
_contextParameters.Insert(index, newParameter); | ||
} | ||
|
||
/// <summary> | ||
/// The place to create the mocked instance with the used provider | ||
/// </summary> | ||
/// <param name="parameter">The parameter that should be mocked</param> | ||
/// <returns>The mocked instance</returns> | ||
protected abstract ContextInstance CreateMockedInstance(ParameterInfo parameter); | ||
|
||
private void ConsiderInitialization() | ||
{ | ||
if (_initialized) return; | ||
|
||
try | ||
{ | ||
var parameters = GetParameters(); | ||
InitializeContextParameters(parameters); | ||
} | ||
finally | ||
{ | ||
_initialized = true; | ||
} | ||
} | ||
|
||
private IEnumerable<ParameterInfo> GetParameters() | ||
{ | ||
var constructor = _reflectionHelper.GetConstructor<TContext>(); | ||
var parameters = _reflectionHelper.GetParametersFor(constructor); | ||
|
||
return parameters; | ||
} | ||
|
||
private void InitializeContextParameters(IEnumerable<ParameterInfo> parameters) | ||
{ | ||
foreach (var parameter in parameters) | ||
{ | ||
var contextParameter = InitializeContextParameterFor(parameter); | ||
_contextParameters.Add(contextParameter); | ||
} | ||
} | ||
|
||
private ContextParameter InitializeContextParameterFor(ParameterInfo parameter) | ||
{ | ||
var instance = CreateInstance(parameter); | ||
var contextParameter = new ContextParameter( | ||
parameter.ParameterType, | ||
parameter.Name, | ||
instance); | ||
|
||
return contextParameter; | ||
} | ||
|
||
private ContextInstance CreateInstance(ParameterInfo parameter) | ||
{ | ||
try | ||
{ | ||
if (parameter.IsOptional && | ||
parameter.DefaultValue == null && | ||
!_includeOptionalParameters) | ||
return new ContextInstance(null, null); | ||
|
||
var parameterType = parameter.ParameterType; | ||
|
||
if (parameterType.IsPrimitive || | ||
parameterType.IsValueType) | ||
return CreateInstanceFromActivator(parameterType); | ||
|
||
// because strings are _extra_ | ||
if (parameterType == typeof(string)) | ||
return new ContextInstance(default(string), null); | ||
|
||
var instance = CreateMockedInstance(parameter); | ||
return instance; | ||
} | ||
catch (Exception ex) | ||
{ | ||
throw new InstanceCreationFailedException(parameter.Name, ex); | ||
} | ||
} | ||
|
||
private ContextInstance CreateInstanceFromActivator(Type type) | ||
{ | ||
var instance = Activator.CreateInstance(type); | ||
return new ContextInstance(instance, null); | ||
} | ||
} | ||
} |
60 changes: 60 additions & 0 deletions
60
src/Core/ArrangeContext.Core/ArrangeContextBaseWithBaseMethods.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
namespace ArrangeContext.Core | ||
{ | ||
/// <inheritdoc/> | ||
public abstract class ArrangeContextBaseWithBaseMethods<TContext> : ArrangeContextBase<TContext> where TContext : class | ||
{ | ||
/// <inheritdoc/> | ||
protected ArrangeContextBaseWithBaseMethods(bool includeOptionalParameters) | ||
:base(includeOptionalParameters) | ||
{ | ||
} | ||
|
||
/// <summary> | ||
/// Replaces the previously generated parameter indicated by <typeparamref name="T"/> with the provided <paramref name="mockedInstance"/> | ||
/// </summary> | ||
/// <typeparam name="T">Indicates the parameter that should be replaced</typeparam> | ||
/// <param name="mockedInstance">The new instance for the parameter with the type <typeparamref name="T"/></param> | ||
public void Use<T>(T mockedInstance) where T : class | ||
{ | ||
var parameter = GetParameter<T>(); | ||
ReplaceInstance(parameter, mockedInstance, mockedInstance); | ||
} | ||
|
||
/// <summary> | ||
/// Replaces the previously generated parameter indicated by the <paramref name="parameterName"/> with the provided <paramref name="mockedInstance"/> | ||
/// </summary> | ||
/// <typeparam name="T">Indicates the type of the parameter that should be replaced</typeparam> | ||
/// <param name="parameterName">The name of the parameter that should be replaced</param> | ||
/// <param name="mockedInstance">The new instance for the parameter with the name <paramref name="parameterName"/></param> | ||
public void Use<T>(T mockedInstance, string parameterName) where T : class | ||
{ | ||
var parameter = GetParameter(parameterName); | ||
ReplaceInstance(parameter, mockedInstance, mockedInstance); | ||
} | ||
|
||
/// <summary> | ||
/// Returns the mocked instance for the parameter indicated by <typeparamref name="T"/> | ||
/// </summary> | ||
/// <typeparam name="T">Indicates the type of the parameter that should be returned</typeparam> | ||
/// <returns>Returns the mocked instance for the parameter indicated by <typeparamref name="T"/></returns> | ||
public T For<T>() where T : class | ||
{ | ||
var parameter = GetParameter<T>(); | ||
var determinedMockedInstance = parameter.Instance.MockedInstance; | ||
return (T)determinedMockedInstance; | ||
} | ||
|
||
/// <summary> | ||
/// Returns the mocked instance for the parameter indicated by the <paramref name="parameterName"/> | ||
/// </summary> | ||
/// <typeparam name="T">Indicates the type of the parameter that should be returned</typeparam> | ||
/// <param name="parameterName">Indicates the name of the parameter that should be returned</param> | ||
/// <returns>Returns the mocked instance for the parameter indicated by <typeparamref name="T"/> and <paramref name="parameterName"/></returns> | ||
public T For<T>(string parameterName) where T : class | ||
{ | ||
var parameter = GetParameter(parameterName); | ||
var determinedMockedInstance = parameter.Instance.MockedInstance; | ||
return (T)determinedMockedInstance; | ||
} | ||
} | ||
} |
Oops, something went wrong.