简体   繁体   English

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

[英]Could not load type Castle.Proxies.IReadinessProxy when running xUnit integration tests in parallel with Autofac

I am having an issue that I've been many days unable to resolve.我有一个问题,我已经很多天无法解决了。 I use xUnit with a given-then-when abstraction to make tests more readable.我将 xUnit 与 given-then-when 抽象结合使用,以使测试更具可读性。

I am using a wrapper over EventStore and running some integration tests.我在 EventStore 上使用包装器并运行一些集成测试。 They all go well.. except one that fails when running all in parallel (and xUnit runs in parallel), but if I run them all sequentially they all succeed.它们都是 go 好.. 除了一个在并行运行时失败(并且 xUnit 并行运行),但如果我按顺序运行它们它们都会成功。

I cannot understand why this would be an issue because every single fact is supposed to run the constructor (the given) and the functionality to test (the when).我不明白为什么这会成为一个问题,因为每个事实都应该运行构造函数(给定的)和测试的功能(何时)。 And in each fact I instantiate an Autofac ContainerBuilder , build the container and resolve its IComponentContext , so in theory every test should be isoalted and idempotent as intended.在每个事实中,我实例化一个 Autofac ContainerBuilder ,构建容器并解析它的IComponentContext ,所以理论上每个测试都应该按预期进行隔离和幂等。

This is the exception I keep receiving:这是我不断收到的例外情况:

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

This is the test with only one Fact that fails when running in parallel with others :这是只有一个 Fact 在与其他 Fact 并行运行时失败的测试

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();
    }
}

Any clue what may be happening here?任何线索可能在这里发生什么? I've had a look at Autofac's guidelines on concurrency: https://autofaccn.readthedocs.io/en/latest/advanced/concurrency.html but I think I am already using properly the component context.我看过 Autofac 的并发指南: https://autofaccn.readthedocs.io/en/latest/advanced/concurrency.html但我认为我已经正确使用了组件上下文。

UPDATE 1: FYI this is the GivenThenWhen template I use.更新 1:仅供参考,这是我使用的 GivenThenWhen 模板。 But nothing very special here (I think!)但这里没什么特别的(我认为!)

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()
        {
        }
    }
}

UPDATE 2: And this is the IoCC Autofac registration's method I am using for my implementation and all the tests.更新 2:这是我用于实现和所有测试的 IoCC Autofac 注册方法。 A bit complex because I use reflection to keep the EventStore wrapper fully generic, but just in case somebody sees something funky there that might affect the tests..有点复杂,因为我使用反射来保持 EventStore 包装器完全通用,但以防万一有人在那里看到可能影响测试的时髦东西。

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>();
    }
}

UPDATE 3: This is the whole repository in case somebody is bored and wants to try to reproduce it.更新 3:这是整个存储库,以防有人感到无聊并想尝试重现它。 It is a generic event store wrapper used for event sourcing implemented for Greg Young's Event Store product using the official C# driver.它是一个通用事件存储包装器,用于使用官方 C# 驱动程序为 Greg Young 的事件存储产品实现的事件源。

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

Funnily enough, this issue seems to disappear every once in a while.有趣的是,这个问题似乎每隔一段时间就会消失一次。 In fact, often, after restarting the PC all the tests pass properly.事实上,通常在重新启动 PC 后,所有测试都会正常通过。 Other times it doesn't so I suspect it has something to do with the fact that I'm loading assemblies at runtime and something gets out of whack:(其他时候它没有,所以我怀疑它与我在运行时加载程序集并且有些东西不正常的事实有关:(


UPDATE 9 Sep 2021 The async execution I was doing is not entirely correct. 2021 年 9 月 9 日更新我执行的异步操作并不完全正确。 There is a way to embrace async execution with xUnit that may have been playing a role in this race condition.有一种方法可以使用 xUnit 进行异步执行,这可能在这种竞争条件下发挥了作用。 This is a better implementation of the given-when-then pattern all my tests inherit from.这是我所有测试继承自的given-when-then模式的更好实现。 Thanks to IAsyncLifetime I am able to asynchronously (and await) the preconditions and the action itself.感谢IAsyncLifetime ,我能够异步(并等待)先决条件和操作本身。

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();
    }
}

DynamicProxyGenAssembly2 is a temporary assembly built by mocking systems that use CastleProxy. DynamicProxyGenAssembly2 是由使用 CastleProxy 的模拟系统构建的临时程序集。 There are some similar opened issues for NSubstitute and Moq that indicate that the problem is a race condition in Castle.Core or even in the .Net Framework (see: TypeLoadException or BadImageFormatException under heavy multi-threaded proxy generation for more details) NSubstitute 和 Moq 有一些类似的未解决问题,表明该问题是 Castle.Core 甚至 .Net Framework 中的竞争条件(有关更多详细信息,请参阅: TypeLoadException 或 BadImageFormatException 在繁重的多线程代理生成下)

The inner exeption indicates that a type could not be loaded内部异常表示无法加载类型

Could not load type 'Castle.Proxies.IReadinessProxy' from assembly 'DynamicProxyGenAssembly2, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'.无法从程序集“DynamicProxyGenAssembly2,版本=0.0.0.0,Culture=neutral,PublicKeyToken=null”加载类型“Castle.Proxies.IReadinessProxy”。

and it came from this line :它来自这一行:

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()

which corresponds to this line in your code对应于您代码中的这一行

var domainEventTypes = assembliesLoaded
                          .SelectMany(s => s.GetTypes())
                          .Where(x => typeof(IDomainEvent).IsAssignableFrom(x)
                                      && x.IsClass);

It means that one of the assembly contains a type that contains a reference to another assembly which is not loaded.这意味着其中一个程序集包含一种类型,该类型包含对未加载的另一个程序集的引用。 Based on the name of the type in error it seems to be related with castle auto generated type.根据错误类型的名称,它似乎与城堡自动生成的类型有关。

You can subscribe to the static AppDomain.CurrentDomain.AssemblyResolve and AppDomain.CurrentDomain.TypeResolve events to better understand why the assembly is not loaded and maybe load it manually.您可以订阅静态AppDomain.CurrentDomain.AssemblyResolveAppDomain.CurrentDomain.TypeResolve事件,以更好地了解未加载程序集的原因,也可以手动加载它。 See Assembly.GetTypes() - ReflectionTypeLoadException for more information.有关详细信息,请参阅Assembly.GetTypes() - ReflectionTypeLoadException

In some case the exception is "normal" and could be ignored with a code like this :在某些情况下,异常是“正常的”,可以使用如下代码忽略:

public static IEnumerable<Type> GetLoadableTypes(this Assembly assembly)
{
    // TODO: Argument validation
    try
    {
        return assembly.GetTypes();
    }
    catch (ReflectionTypeLoadException e)
    {
        return e.Types.Where(t => t != null);
    }
}

Code from How to prevent ReflectionTypeLoadException when calling Assembly.GetTypes() 调用 Assembly.GetTypes() 时如何防止 ReflectionTypeLoadException 的代码

And you can use this here :你可以在这里使用它:

var domainEventTypes = assembliesLoaded
                          .SelectMany(s => s.GetLoadableTypes())
                          .Where(x => typeof(IDomainEvent).IsAssignableFrom(x)
                                      && x.IsClass);

If you have COM dependencies, make sure you set Embed Interop Types to No in your unit tests projects for these COM dependencies.如果您有 COM 依赖项,请确保在这些 COM 依赖项的单元测试项目中将嵌入互操作类型设置为 By default, this property is set to Yes.默认情况下,此属性设置为 Yes。 When you mockup objects in your unit tests from these COM objects, they should not be embedded.当您从这些 COM 对象中模拟单元测试中的对象时,不应嵌入它们。

嵌入互操作类型

your table of database id DatabaseGeneratedOption.Identity and you try to save id of code you get error您的数据库 ID 表 DatabaseGeneratedOption.Identity 并且您尝试保存代码的 ID 时出现错误

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 当知道 xUnit 并行运行时,使用 xUnit 进行集成测试的策略是什么? - What strategy to use with xUnit for integration tests when knowing they run in parallel? xunit“无法加载类型”错误 - xunit “could not load type” error Cake Build脚本:运行XUnit集成测试时出错-无法获取ConfigurationManager.ConnectionStrings [“ FlexConnString”] - Cake Build Script: Error when running XUnit Integration Tests - Cannot get ConfigurationManager.ConnectionStrings[“FlexConnString”] 没有xUnit DNX的并行测试 - No parallel tests with xUnit DNX Autofac异常:无法从程序集中加载类型 - Autofac Exception: Could not load type from assembly 当类继承某个类时,无法并行运行xunit测试 - Cant run xunit tests in parallel when classes are inheriting some class 无法加载类型“ Castle.Core.Internal.SlimReaderWriterLock” - Could not load type 'Castle.Core.Internal.SlimReaderWriterLock' 运行 xUnit.net 测试时的运行时文件夹结构 - Runtime folder structure when running xUnit.net tests 构建后xUnit测试未运行 - xUnit Tests not running after building 测试 .NET 框架时,在 .NET Core xUnit 测试项目中发现无法加载类型“System.Web.HttpContextBase”错误 - Could not load type 'System.Web.HttpContextBase' error found in .NET Core xUnit test project when testing a .NET Framework
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM