我试图理解为什么我无法 AutoMock.Mock 接口,以及非接口(如字符串键)或委托(如 Foo.Factory)的存在是否是问题的一部分以及如何处理它们。
这里是尝试在 AutoMock 中使用委托工厂的简短示例。
几个问题:
这是一个 XUnit 示例,其中包含我尝试过的几种容器注册组合。
希望有人能解释委托工厂用法的微妙之处和陷阱,因为我以比这个简单示例更嵌套的方式使用它们,我想特别了解我在从不同接口级别获取模拟时做错了什么.
using Autofac;
using Autofac.Core;
using Autofac.Extras.Moq;
using Divergic.Logging.Xunit;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Moq;
using System;
using Xunit;
using Xunit.Abstractions;
namespace MyTesting
{
public class SimpleTests
{
public interface IFooProvider
{
IFoo GetFoo(string key);
}
public interface IFoo
{
string Key { get; }
}
public class FooProvider : IFooProvider
{
ILogger<FooProvider> Logger { get; init; }
IConfiguration Config { get; init; }
Foo.Factory Factory { get; init; }
public FooProvider(ILogger<FooProvider> logger, IConfiguration config, Foo.Factory factory)
{
Logger = logger;
Config = config;
Factory = factory;
}
public IFoo GetFoo(string key) => Factory(key);
}
public class Foo : IFoo
{
ILogger Logger { get; init; }
IConfiguration Config { get; init; }
public string Key { get; init; }
public delegate Foo Factory(string key);
public Foo(ILogger logger, IConfiguration config, string key)
{
Logger = logger;
Config = config;
Key = key;
}
}
private ILogger Log { get; init; }
public SimpleTests(ITestOutputHelper output)
{
Log = LogFactory.Create(output).CreateLogger(GetType().Name);
}
[Fact]
public void TestMe()
{
Action<int, bool, bool> action = (pass, registerFooAsSelf, registerFooFactory) =>
{
Log.LogInformation($"\n\n======== Pass {pass}: {(registerFooAsSelf?"Register<Foo>.AsSelf()":"")} {(registerFooFactory?"Register<Foo.Factory>(c => ...)":"")} ========");
AutoMock Mock = AutoMock.GetStrict(cfg =>
{
cfg.RegisterType<FooProvider>().SingleInstance().AsSelf();
cfg.RegisterType<Foo>().As<IFoo>();
if (registerFooAsSelf)
// Necessary for delegate factory? otherwise foo3 ends up as "test2" instead of "test3"
cfg.RegisterType<Foo>().AsSelf();
if (registerFooFactory)
// I assume this is equivalent to the auto-generated impl and can be omitted unless I had custom logic
cfg.Register<Foo.Factory>(c =>
{
var loggerFactory = c.Resolve<ILoggerFactory>();
var config = c.Resolve<IConfiguration>();
var foo = c.Resolve<Func<ILoggerFactory, IConfiguration, string, Foo>>();
return (string key) => foo(loggerFactory, config, key);
});
});
var logger = Mock.Mock<ILogger>();
var config = Mock.Mock<IConfiguration>();
var p1 = Mock.Mock<IFooProvider>();
p1.Setup(p => p.GetFoo(It.IsAny<string>())).Returns((string s) => new Foo(logger.Object, config.Object, s));
var foo1 = p1.Object.GetFoo("test1");
Log.LogInformation($"foo1={foo1.Key}");
var p2 = Mock.Create<FooProvider>();
var foo2 = p2.GetFoo("test2");
Log.LogInformation($"foo2={foo2.Key}");
var foo3 = Mock.Create<Foo.Factory>()("test3");
Log.LogInformation($"foo3={foo3.Key} {(!"test3".Equals(foo3.Key)?" ** != **":"")}");
// But how do I generate a mock of IFoo?
// Autofac.Core.DependencyResolutionException : An exception was thrown while activating Microsoft.CCI.Common.Tests.Authentication.SimpleTests + Foo.
//---- Autofac.Core.DependencyResolutionException : None of the constructors found with 'Autofac.Core.Activators.Reflection.DefaultConstructorFinder'
// on type 'Microsoft.CCI.Common.Tests.Authentication.SimpleTests+Foo' can be invoked with the available services and parameters:
// Cannot resolve parameter 'System.String key' of constructor 'Void .ctor(Microsoft.Extensions.Logging.ILogger, Microsoft.Extensions.Configuration.IConfiguration, System.String)'.
LogThrow<DependencyResolutionException>(() =>
{
var foo4 = Mock.Mock<IFoo>();
}, " foo4: Error expected, since string key is not registered");
// System.InvalidCastException : Unable to cast object of type 'Foo' to type 'Moq.IMocked`1[Microsoft.CCI.Common.Tests.Authentication.SimpleTests+IFoo]'.
LogThrow<InvalidCastException>(() =>
{
var mockFoo5 = Mock.Mock<IFoo>(
new TypedParameter(typeof(ILogger), logger.Object),
new TypedParameter(typeof(IConfiguration), config.Object),
new TypedParameter(typeof(string), "test5"));
}, " foo5: Error not expected, all ctor or delegate Factory params are given, so why doesn't this work?");
};
action(1, false, false);
action(2, false, true);
action(3, true, false);
action(4, true, true);
}
private void LogThrow<T>(Action action, string msg) where T : Exception
{
Assert.Throws<T>(() =>
{
try
{
action();
}
catch (T ex)
{
//Log.LogError(ex, msg);
Log.LogError($"{ex.GetType().Name}: {ex.Message}\n" +
$"{(ex.InnerException != null ? $"{ex.InnerException.GetType().Name}: {ex.InnerException.Message}\n" : "")}{msg}");
throw;
}
});
}
}
}
输出:
======== Pass 1: ========
Information [0]: foo1=test1
Information [0]: foo2=test2
Information [0]: foo3=test2 ** != **
Error [0]: DependencyResolutionException: An exception was thrown while activating Microsoft.CCI.Common.Tests.Authentication.SimpleTests+Foo.
DependencyResolutionException: None of the constructors found with 'Autofac.Core.Activators.Reflection.DefaultConstructorFinder' on type 'Microsoft.CCI.Common.Tests.Authentication.SimpleTests+Foo' can be invoked with the available services and parameters:
Cannot resolve parameter 'System.String key' of constructor 'Void .ctor(Microsoft.Extensions.Logging.ILogger, Microsoft.Extensions.Configuration.IConfiguration, System.String)'.
foo4: Error expected, since string key is not registered
Error [0]: InvalidCastException: Unable to cast object of type 'Foo' to type 'Moq.IMocked`1[Microsoft.CCI.Common.Tests.Authentication.SimpleTests+IFoo]'.
foo5: Error not expected, all ctor or delegate Factory params are given, so why doesn't this work?
======== Pass 2: Register<Foo.Factory>(c => ...) ========
Information [0]: foo1=test1
Information [0]: foo2=test2
Information [0]: foo3=test2 ** != **
Error [0]: DependencyResolutionException: An exception was thrown while activating Microsoft.CCI.Common.Tests.Authentication.SimpleTests+Foo.
DependencyResolutionException: None of the constructors found with 'Autofac.Core.Activators.Reflection.DefaultConstructorFinder' on type 'Microsoft.CCI.Common.Tests.Authentication.SimpleTests+Foo' can be invoked with the available services and parameters:
Cannot resolve parameter 'System.String key' of constructor 'Void .ctor(Microsoft.Extensions.Logging.ILogger, Microsoft.Extensions.Configuration.IConfiguration, System.String)'.
foo4: Error expected, since string key is not registered
Error [0]: InvalidCastException: Unable to cast object of type 'Foo' to type 'Moq.IMocked`1[Microsoft.CCI.Common.Tests.Authentication.SimpleTests+IFoo]'.
foo5: Error not expected, all ctor or delegate Factory params are given, so why doesn't this work?
======== Pass 3: Register<Foo>.AsSelf() ========
Information [0]: foo1=test1
Information [0]: foo2=test2
Information [0]: foo3=test3
Error [0]: DependencyResolutionException: An exception was thrown while activating Microsoft.CCI.Common.Tests.Authentication.SimpleTests+Foo.
DependencyResolutionException: None of the constructors found with 'Autofac.Core.Activators.Reflection.DefaultConstructorFinder' on type 'Microsoft.CCI.Common.Tests.Authentication.SimpleTests+Foo' can be invoked with the available services and parameters:
Cannot resolve parameter 'System.String key' of constructor 'Void .ctor(Microsoft.Extensions.Logging.ILogger, Microsoft.Extensions.Configuration.IConfiguration, System.String)'.
foo4: Error expected, since string key is not registered
Error [0]: InvalidCastException: Unable to cast object of type 'Foo' to type 'Moq.IMocked`1[Microsoft.CCI.Common.Tests.Authentication.SimpleTests+IFoo]'.
foo5: Error not expected, all ctor or delegate Factory params are given, so why doesn't this work?
======== Pass 4: Register<Foo>.AsSelf() Register<Foo.Factory>(c => ...) ========
Information [0]: foo1=test1
Information [0]: foo2=test2
Information [0]: foo3=test3
Error [0]: DependencyResolutionException: An exception was thrown while activating Microsoft.CCI.Common.Tests.Authentication.SimpleTests+Foo.
DependencyResolutionException: None of the constructors found with 'Autofac.Core.Activators.Reflection.DefaultConstructorFinder' on type 'Microsoft.CCI.Common.Tests.Authentication.SimpleTests+Foo' can be invoked with the available services and parameters:
Cannot resolve parameter 'System.String key' of constructor 'Void .ctor(Microsoft.Extensions.Logging.ILogger, Microsoft.Extensions.Configuration.IConfiguration, System.String)'.
foo4: Error expected, since string key is not registered
Error [0]: InvalidCastException: Unable to cast object of type 'Foo' to type 'Moq.IMocked`1[Microsoft.CCI.Common.Tests.Authentication.SimpleTests+IFoo]'.
foo5: Error not expected, all ctor or delegate Factory params are given, so why doesn't this work?