Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#184: Cache instances while setting up the ctor #185

Merged
merged 1 commit into from
Jan 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not: Not sure I like the name TryCache on a method that doesn't return a book. Perhaps more like CacheIfNotFound?

{
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