尝试使用非接口或委托工厂参数进行 AutoMock.Mock<T> 的问题

问题描述 投票:0回答:0

我试图理解为什么我无法 AutoMock.Mock 接口,以及非接口(如字符串键)或委托(如 Foo.Factory)的存在是否是问题的一部分以及如何处理它们。

这里是尝试在 AutoMock 中使用委托工厂的简短示例。

几个问题:

  1. 为什么缺少 Register.AsSelf() 会导致 foo3.Key 为“test2”而不是“test3”?
  2. 如何生成 AutoMock.Mock?我没有尝试任何参数(我可以理解它不起作用,因为字符串键至少没有注册)。我还为所有 3 个可能的参数尝试了 TypedParameters(对于普通的 Foo ctor 或委托工厂,因为我不知道它尝试了哪个)。

这是一个 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?

c# autofac xunit.net automoq
© www.soinside.com 2019 - 2024. All rights reserved.