Skip to content

Commit

Permalink
#184: Cache instances while setting up the ctor (#185)
Browse files Browse the repository at this point in the history
  • Loading branch information
adamhewitt627 committed Jan 4, 2023
1 parent 629bf06 commit 18add29
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 25 deletions.
24 changes: 23 additions & 1 deletion Moq.AutoMock.Tests/DescribeCreateInstance.cs
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,6 @@ public void It_throws_when_creating_object_with_recursive_dependency()
Assert.IsTrue(e.Message.StartsWith($"Did not find a best constructor for `{typeof(WithRecursiveDependency)}`"));
}


[TestMethod]
[Description("Issue 123")]
public void It_can_use_fixed_value_to_supply_string_parameter()
Expand All @@ -176,6 +175,24 @@ public void It_can_use_custom_resolver_to_supply_string_parameter()
Assert.AreEqual("Test string", sut.String);
}

[TestMethod]
public void ConcreteDependencyFirst_WhenServiceIsShared_UsesResolvedInstance()
{
AutoMocker mocker = new();
ConcreteDependencyIsFirst constructed = mocker.CreateInstance<ConcreteDependencyIsFirst>();

Assert.AreSame(constructed.Service, constructed.Dependency.Service);
}

[TestMethod]
public void ConcreteDependency_WhenServiceIsShared_UsesResolvedInstance()
{
AutoMocker mocker = new();
ConcreteDependencyIsSecond constructed = mocker.CreateInstance<ConcreteDependencyIsSecond>();

Assert.AreSame(constructed.Service, constructed.Dependency.Service);
}

private class CustomStringResolver : IMockResolver
{
public CustomStringResolver(string stringValue)
Expand Down Expand Up @@ -203,4 +220,9 @@ public HasStringParameter(string @string)

public string String { get; }
}


public record class ConcreteDependency(IService1 Service);
public record class ConcreteDependencyIsFirst(ConcreteDependency Dependency, IService1 Service);
public record class ConcreteDependencyIsSecond(IService1 Service, ConcreteDependency Dependency);
}
39 changes: 15 additions & 24 deletions Moq.AutoMock/AutoMocker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -225,8 +225,6 @@ public object CreateInstance(Type type, bool enablePrivate)
nameof(type));
}

CacheInstances(arguments.Zip(ctor.GetParameters(), (i, p) => (p.ParameterType, i)));

try
{
object?[] parameters = arguments.Select(x => x.Value).ToArray();
Expand Down Expand Up @@ -958,7 +956,6 @@ public void Verify<T, TResult>(Expression<Func<T, TResult>> expression, Times ti
TryGetConstructorInvocation(serviceType, objectGraphContext, out ConstructorInfo? ctor, out IInstance[]? arguments))
{
constructorArgs = arguments.Select(x => x.Value).ToArray();
CacheInstances(arguments.Zip(ctor.GetParameters(), (i, p) => (p.ParameterType, i)));
}

if (Activator.CreateInstance(mockType, mockBehavior, constructorArgs) is Mock mock)
Expand Down Expand Up @@ -1018,43 +1015,37 @@ bool TryCreateArguments(ConstructorInfo constructor, ObjectGraphContext context,
{
return false;
}

TryCache(parameters[i].ParameterType, service);
arguments[i] = service;
}
return true;
}

}

private void TryCache(Type type, IInstance instance)
{
WithTypeMap(typeMap =>
{
if (!typeMap.TryGetValue(type, out _))
{
typeMap[type] = instance;
}
});
}

private Mock GetOrMakeMockFor(Type type)
{
if (TryResolve(type, new ObjectGraphContext(false), out IInstance? instance) &&
instance is MockInstance mockInstance)
{
WithTypeMap(typeMap =>
{
if (!typeMap.ContainsKey(type))
{
typeMap[type] = mockInstance;
}
});
TryCache(type, mockInstance);
return mockInstance.Mock;
}
throw new ArgumentException($"{type} does not resolve to a Mock");
}

internal void CacheInstances(IEnumerable<(Type, IInstance)> instances)
{
WithTypeMap(typeMap =>
{
foreach (var (type, instance) in instances)
{
if (!typeMap.ContainsKey(type))
{
typeMap[type] = instance;
}
}
});
}

private void WithTypeMap(Action<NonBlocking.ConcurrentDictionary<Type, IInstance>> onTypeMap)
{
if (TypeMap is { } typeMap)
Expand Down

0 comments on commit 18add29

Please sign in to comment.