diff --git a/Moq.AutoMock.Tests/DescribeCreateInstance.cs b/Moq.AutoMock.Tests/DescribeCreateInstance.cs index 2c85de9c..7049c43b 100644 --- a/Moq.AutoMock.Tests/DescribeCreateInstance.cs +++ b/Moq.AutoMock.Tests/DescribeCreateInstance.cs @@ -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() @@ -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(); + + Assert.AreSame(constructed.Service, constructed.Dependency.Service); + } + + [TestMethod] + public void ConcreteDependency_WhenServiceIsShared_UsesResolvedInstance() + { + AutoMocker mocker = new(); + ConcreteDependencyIsSecond constructed = mocker.CreateInstance(); + + Assert.AreSame(constructed.Service, constructed.Dependency.Service); + } + private class CustomStringResolver : IMockResolver { public CustomStringResolver(string stringValue) @@ -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); } diff --git a/Moq.AutoMock/AutoMocker.cs b/Moq.AutoMock/AutoMocker.cs index e3f2ba02..4931a2bb 100644 --- a/Moq.AutoMock/AutoMocker.cs +++ b/Moq.AutoMock/AutoMocker.cs @@ -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(); @@ -958,7 +956,6 @@ public void Verify(Expression> 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) @@ -1018,10 +1015,24 @@ 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) @@ -1029,32 +1040,12 @@ 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> onTypeMap) { if (TypeMap is { } typeMap)