Skip to content

Commit

Permalink
#9: Redoes the Extension-Method-Approach
Browse files Browse the repository at this point in the history
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
wgnf committed Apr 4, 2021
1 parent 51ab2a1 commit bded34e
Show file tree
Hide file tree
Showing 23 changed files with 438 additions and 744 deletions.
93 changes: 0 additions & 93 deletions src/Core/ArrangeContext.Core/ArrangeContext.cs

This file was deleted.

191 changes: 191 additions & 0 deletions src/Core/ArrangeContext.Core/ArrangeContextBase.cs
@@ -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&lt;Something&gt;)</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 src/Core/ArrangeContext.Core/ArrangeContextBaseWithBaseMethods.cs
@@ -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;
}
}
}

0 comments on commit bded34e

Please sign in to comment.