首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >在与Autofac并行运行xUnit集成测试时,无法加载类型Castle.Proxies.IReadinessProxy

在与Autofac并行运行xUnit集成测试时,无法加载类型Castle.Proxies.IReadinessProxy
EN

Stack Overflow用户
提问于 2019-05-22 18:37:24
回答 2查看 2.9K关注 0票数 1

我有一个问题,我已经有很多天无法解决了。为了使测试更具可读性,我使用xUnit和一个给定的、然后是何时的抽象。

我在EventStore上使用了一个包装器并运行了一些集成测试。他们都很顺利..除了,它在并行运行(和xUnit并行运行)时都会失败,但如果我按顺序运行它们,它们都会成功。

我不明白为什么这会成为一个问题,因为每个事实都应该运行构造函数(给定的)和测试的功能(何时)。在每个事实中,我实例化了一个Autofac ContainerBuilder,构建了容器并解析了它的IComponentContext,所以理论上每个测试都应该像预期的那样是等价性和幂等性的。

这是我不断收到的异常:

代码语言:javascript
复制
Autofac.Core.DependencyResolutionException : An exception was thrown while activating SalesOrder.EventStore.Infra.EventStore.EventStore -> SalesOrder.EventStore.Infra.EventStore.DomainEventsRetriever -> SalesOrder.EventStore.Infra.EventStore.Factories.DomainEventFactory -> λ:SalesOrder.EventStore.Infra.EventStore.EventTypeResolver.
---- System.Reflection.ReflectionTypeLoadException : Unable to load one or more of the requested types.
Could not load type 'Castle.Proxies.IReadinessProxy' from assembly 'DynamicProxyGenAssembly2, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'.
   at Autofac.Core.Resolving.InstanceLookup.Activate(IEnumerable`1 parameters, Object& decoratorTarget) in C:\projects\autofac\src\Autofac\Core\Resolving\InstanceLookup.cs:line 136
   at Autofac.Core.Resolving.InstanceLookup.Execute() in C:\projects\autofac\src\Autofac\Core\Resolving\InstanceLookup.cs:line 85
   at Autofac.Core.Resolving.ResolveOperation.GetOrCreateInstance(ISharingLifetimeScope currentOperationScope, IComponentRegistration registration, IEnumerable`1 parameters) in C:\projects\autofac\src\Autofac\Core\Resolving\ResolveOperation.cs:line 130
   at Autofac.Core.Resolving.ResolveOperation.Execute(IComponentRegistration registration, IEnumerable`1 parameters) in C:\projects\autofac\src\Autofac\Core\Resolving\ResolveOperation.cs:line 83
   at Autofac.ResolutionExtensions.TryResolveService(IComponentContext context, Service service, IEnumerable`1 parameters, Object& instance) in C:\projects\autofac\src\Autofac\ResolutionExtensions.cs:line 1041
   at Autofac.ResolutionExtensions.ResolveService(IComponentContext context, Service service, IEnumerable`1 parameters) in C:\projects\autofac\src\Autofac\ResolutionExtensions.cs:line 871
   at Autofac.ResolutionExtensions.Resolve[TService](IComponentContext context, IEnumerable`1 parameters) in C:\projects\autofac\src\Autofac\ResolutionExtensions.cs:line 300
   at SalesOrder.EventStore.Infra.EventStore.Autofac.IntegrationTests.EventStoreExtensionsTests.ResolveTests.Given_A_Container_With_Event_Store_Registered_When_Resolving_An_IEventStore.When() in C:\src\SalesOrder.EventStore\SalesOrder.EventStore.Infra.EventStore.Autofac.IntegrationTests\EventStoreExtensionsTests\ResolveTests.cs:line 53
   at SalesOrder.EventStore.Infra.EventStore.Autofac.IntegrationTests.EventStoreExtensionsTests.ResolveTests.Given_A_Container_With_Event_Store_Registered_When_Resolving_An_IEventStore..ctor()
----- Inner Stack Trace -----
   at System.Reflection.RuntimeModule.GetTypes(RuntimeModule module)
   at System.Reflection.RuntimeModule.GetTypes()
   at System.Reflection.Assembly.GetTypes()
   at SalesOrder.EventStore.Infra.EventStore.Autofac.EventStoreExtensions.<>c.<RegisterResolvers>b__6_2(Assembly s) in C:\src\SalesOrder.EventStore\SalesOrder.EventStore.Infra.EventStore.Autofac\EventStoreExtensions.cs:line 174
   at System.Linq.Enumerable.SelectManySingleSelectorIterator`2.MoveNext()
   at System.Linq.Enumerable.WhereEnumerableIterator`1.MoveNext()
   at System.Collections.Generic.List`1.AddEnumerable(IEnumerable`1 enumerable)
   at SalesOrder.EventStore.Infra.EventStore.Factories.EventTypeResolverFactory.Create(IEnumerable`1 existingTypes, IReadOnlyDictionary`2 domainEventSerializerDeserializers) in C:\src\SalesOrder.EventStore\SalesOrder.EventStore.Infra.EventStore\Factories\EventTypeResolverFactory.cs:line 16
   at SalesOrder.EventStore.Infra.EventStore.Autofac.EventStoreExtensions.<>c__DisplayClass6_0.<RegisterResolvers>b__1(IComponentContext context) in C:\src\SalesOrder.EventStore\SalesOrder.EventStore.Infra.EventStore.Autofac\EventStoreExtensions.cs:line 180
   at Autofac.Builder.RegistrationBuilder.<>c__DisplayClass0_0`1.<ForDelegate>b__0(IComponentContext c, IEnumerable`1 p) in C:\projects\autofac\src\Autofac\Builder\RegistrationBuilder.cs:line 62
   at Autofac.Core.Activators.Delegate.DelegateActivator.ActivateInstance(IComponentContext context, IEnumerable`1 parameters) in C:\projects\autofac\src\Autofac\Core\Activators\Delegate\DelegateActivator.cs:line 71
   at Autofac.Core.Resolving.InstanceLookup.Activate(IEnumerable`1 parameters, Object& decoratorTarget) in C:\projects\autofac\src\Autofac\Core\Resolving\InstanceLookup.cs:line 118

这是在与其他并行运行时只有一个事实失败的测试

代码语言:javascript
复制
public class Given_A_Container_With_Event_Store_Registered_When_Resolving_An_IEventStore
    : Given_When_Then_Test
{
    private IEventStore _sut;
    private IComponentContext _componentContext;

    protected override void Given()
    {
        var builder = new ContainerBuilder();
        builder
            .RegisterEventStore(
                ctx =>
                {
                    var eventStoreOptions =
                        new EventStoreOptions
                        {
                            Url = EventStoreTestConstants.TestUrl,
                            Port = EventStoreTestConstants.TestPort
                        };
                    return eventStoreOptions;
                },
                ctx =>
                {
                    var readinessOptions =
                        new ReadinessOptions
                        {
                            Timeout = EventStoreTestConstants.TestTimeout
                        };
                    return readinessOptions;
                });

        var container = builder.Build();
        _componentContext = container.Resolve<IComponentContext>();
    }

    protected override void When()
    {
        _sut = _componentContext.Resolve<IEventStore>();
    }

    [Fact]
    public void Then_It_Should_Not_Be_Null()
    {
        _sut.Should().NotBeNull();
    }
}

我看过Autofac关于并发的指南:https://autofaccn.readthedocs.io/en/latest/advanced/concurrency.html,但我认为我已经正确地使用了组件上下文。

更新1:仅供参考这是我使用的GivenThenWhen模板。但这里没什么特别的(我想!)

代码语言:javascript
复制
namespace ToolBelt.TestSupport
{
    public abstract class Given_When_Then_Test
        : IDisposable
    {
        protected Given_When_Then_Test()
        {
            Setup();
        }

        private void Setup()
        {
            Given();
            When();
        }

        protected abstract void Given();

        protected abstract void When();

        public void Dispose()
        {
            Cleanup();
        }

        protected virtual void Cleanup()
        {
        }
    }
    
    public abstract class Given_WhenAsync_Then_Test
        : IDisposable
    {
        protected Given_WhenAsync_Then_Test()
        {
            Task.Run(async () => { await SetupAsync(); }).GetAwaiter().GetResult();
        }

        private async Task SetupAsync()
        {
            Given();
            await WhenAsync();
        }

        protected abstract void Given();

        protected abstract Task WhenAsync();

        public void Dispose()
        {
            Cleanup();
        }

        protected virtual void Cleanup()
        {
        }
    }
}

更新2:,这是我在实现和所有测试中使用的IoCC Autofac注册方法。有点复杂,因为我使用反射来保持EventStore包装器的完全泛型,但以防有人在那里看到一些可能会影响测试的奇怪的东西。

代码语言:javascript
复制
public static class EventStoreExtensions
{
    public static void RegisterEventStore(
        this ContainerBuilder builder,
        Func<IComponentContext, EventStoreOptions> optionsRetriever,
        Func<IComponentContext, ReadinessOptions> readinessOptionsRetriever,
        Func<IComponentContext, CustomDomainEventMappersOptions> customDomainEventMappersOptionsRetriever = null)
    {
        RegisterEventStoreConnection(builder, optionsRetriever);
        RegisterFactories(builder);
        RegisterEventStoreManager(builder);
        RegisterConverters(builder);
        RegisterResolvers(builder, customDomainEventMappersOptionsRetriever);
        RegisterEventStoreServices(builder);
        RegisterEventStoreReadinessCheck(builder, readinessOptionsRetriever);
    }

    private static void RegisterEventStoreReadinessCheck(
        ContainerBuilder builder,
        Func<IComponentContext, ReadinessOptions> readinessOptionsRetriever)
    {
        builder
            .Register(context =>
            {
                var ctx = context.Resolve<IComponentContext>();
                var readinessOptions = readinessOptionsRetriever.Invoke(ctx);

                var timeout = readinessOptions.Timeout;
                var eventStoreConnection = context.Resolve<IEventStoreConnection>();

                var eventStoreReadiness =
                    new EventStoreReadiness(
                        eventStoreConnection,
                        timeout);

                return eventStoreReadiness;

            })
            .As<IEventStoreReadiness>()
            .As<IReadiness>()
            .SingleInstance();
    }

    private static void RegisterEventStoreConnection(
        ContainerBuilder builder,
        Func<IComponentContext, EventStoreOptions> optionsRetriever)
    {
        builder
            .Register(context =>
            {
                var ctx = context.Resolve<IComponentContext>();
                var eventStoreOptions = optionsRetriever.Invoke(ctx);

                ConnectionSettings connectionSetting =
                    ConnectionSettings
                        .Create()
                        .KeepReconnecting();

                var urlString = eventStoreOptions.Url;
                var port = eventStoreOptions.Port;
                var ipAddress = IPAddress.Parse(urlString);
                var tcpEndPoint = new IPEndPoint(ipAddress, port);

                var eventStoreConnection = EventStoreConnection.Create(connectionSetting, tcpEndPoint);
                return eventStoreConnection;
            })
            .As<IEventStoreConnection>()
            .SingleInstance();
    }

    private static void RegisterFactories(
        ContainerBuilder builder)
    {
        builder
            .RegisterType<DomainEventFactory>()
            .As<IDomainEventFactory>()
            .SingleInstance();

        builder
            .RegisterType<EventDataFactory>()
            .As<IEventDataFactory>()
            .SingleInstance();

        builder
            .RegisterType<ExpectedVersionFactory>()
            .As<IExpectedVersionFactory>()
            .SingleInstance();

        builder
            .RegisterType<IdFactory>()
            .As<IIdFactory>()
            .SingleInstance();

        builder
            .RegisterType<StreamNameFactory>()
            .As<IStreamNameFactory>()
            .SingleInstance();

        builder
            .RegisterType<RetrievedEventFactory>()
            .As<IRetrievedEventFactory>()
            .SingleInstance();
    }

    private static void RegisterEventStoreManager(ContainerBuilder builder)
    {
        builder
            .RegisterType<EventStoreManager>()
            .As<IEventStoreManager>()
            .SingleInstance();
    }

    private static void RegisterConverters(ContainerBuilder builder)
    {
        builder
            .Register(context =>
            {
                var utf8Encoding = new BytesConverter(Encoding.UTF8);
                return utf8Encoding;
            })
            .As<IBytesConverter>()
            .SingleInstance();

        builder
            .RegisterType<NewtonsoftConverter>()
            .As<IJsonConverter>()
            .SingleInstance();
    }

    private static void RegisterResolvers(
        ContainerBuilder builder,
        Func<IComponentContext, CustomDomainEventMappersOptions> customDomainEventMappersOptionsRetriever)
    {
        builder
            .Register(context =>
            {
                var ctx = context.Resolve<IComponentContext>();
                var customDomainEventMappersOptions = customDomainEventMappersOptionsRetriever?.Invoke(ctx);
                var domainEventSerializerDeserializers =
                    customDomainEventMappersOptions?.DomainEventSerializerDeserializers;
                var mapperResolver = MapperResolverFactory.Create(domainEventSerializerDeserializers);
                return mapperResolver;
            })
            .As<IMapperResolver>()
            .SingleInstance();

        builder
            .Register(context =>
            {
                var ctx = context.Resolve<IComponentContext>();

                var assembliesLoaded = AppDomain.CurrentDomain.GetAssemblies();
                var domainEventTypes =
                    assembliesLoaded
                        .SelectMany(s => s.GetTypes())
                        .Where(x => typeof(IDomainEvent).IsAssignableFrom(x)
                                    && x.IsClass);
                var customDomainEventMappersOptions = customDomainEventMappersOptionsRetriever?.Invoke(ctx);
                var domainEventSerializerDeserializers =
                    customDomainEventMappersOptions?.DomainEventSerializerDeserializers;
                var typeResolver =
                    EventTypeResolverFactory.Create(
                        domainEventTypes,
                        domainEventSerializerDeserializers);
                return typeResolver;
            })
            .As<IEventTypeResolver>()
            .SingleInstance();
    }

    private static void RegisterEventStoreServices(ContainerBuilder builder)
    {
        builder
            .RegisterType<EventStoreRepository>()
            .As<IEventStoreRepository>();

        builder
            .RegisterType<DomainEventsPersister>()
            .As<IDomainEventsPersister>();

        builder
            .RegisterType<DomainEventsRetriever>()
            .As<IDomainEventsRetriever>();

        builder
            .RegisterType<EventStore>()
            .As<IEventStore>();
    }
}

更新3:这是整个存储库,以防有人感到无聊并想要重现它。它是一个通用的事件存储包装器,用于使用官方的C#驱动程序为Greg Young的event store产品实现事件源。

https://gitlab.com/DiegoDrivenDesign/DiDrDe.EventStore

有趣的是,这个问题似乎偶尔会消失。事实上,通常情况下,在重新启动PC之后,所有测试都会正常通过。其他时候没有,所以我怀疑这与我在运行时加载程序集的事实有关,并且有些东西出了问题:(

更新9 2021年9月我所做的异步执行并不完全正确。有一种方法可以用xUnit拥抱异步执行,这可能在这种争用情况下起到了作用。这是我所有测试继承的given-when-then模式的更好实现。多亏了IAsyncLifetime,我能够异步(并等待)前提条件和操作本身。

代码语言:javascript
复制
using System.Threading.Tasks;
using Xunit;

namespace Rubiko.TestSupport.XUnit
{
    public abstract class Given_When_Then_Test_Async
        : IAsyncLifetime
    {
        public async Task InitializeAsync()
        {
            await Given();
            await When();
        }

        public async Task DisposeAsync()
        {
            await Cleanup();
        }

        protected virtual Task Cleanup()
        {
            return Task.CompletedTask;
        }

        protected abstract Task Given();

        protected abstract Task When();
    }
}
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2019-05-30 16:56:40

DynamicProxyGenAssembly2是由使用CastleProxy的模拟系统构建的临时程序集。对于NSubstitute和Moq,有一些类似的开放问题,表明该问题是Castle.Core中的竞争条件,甚至是.Net框架中的竞争条件(有关更多细节,请参阅:TypeLoadException or BadImageFormatException under heavy multi-threaded proxy generation )

票数 3
EN

Stack Overflow用户

发布于 2021-06-15 22:42:27

如果有COM依赖项,请确保在单元测试项目中为这些COM依赖项将Embed Interop Types设置为No。默认情况下,此属性设置为“是”。当您在单元测试中从这些COM对象模拟对象时,不应该嵌入这些对象。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/56254916

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档