我遇到了一个问题,已经很多天无法解决了。 我将xUnit与给定时间的抽象一起使用,以使测试更具可读性。
我在EventStore上使用包装器并运行一些集成测试。它们都运行良好..除了在全部并行运行时失败(和xUnit并行运行)之外,但是如果我按顺序全部运行它们都将成功。
我不明白为什么这会是一个问题,因为每个事实都应该运行构造函数(给定的)和要测试的功能(何时)。并且在每个事实中,我都实例化了一个Autofac ContainerBuilder
,构建了容器并解析了其IComponentContext
,因此,从理论上讲,每个测试都应按预期进行等值和幂等。
这是我一直收到的例外情况:
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
这是只有一个事实的测试,该事实与其他人并行运行时会失败:
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模板。但是这里没有什么特别的(我想!)
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包装器保持完全通用,但以防万一有人看到一些时髦的东西可能影响测试。
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的事件存储产品实现的事件源。
https://gitlab.com/DiegoDrivenDesign/DiDrDe.EventStore
很有趣的是,这个问题似乎每隔一段时间就会消失一次。实际上,通常在重新启动PC后,所有测试都会正确通过。有时它并没有,所以我怀疑这与我在运行时加载程序集和某些东西脱离了事实有关:(
答案 0 :(得分:1)
内部信息表明无法加载类型
无法从程序集“ DynamicProxyGenAssembly2,Version = 0.0.0.0,Culture = neutral,PublicKeyToken = null”中加载类型'Castle.Proxies.IReadinessProxy'。
它来自此行:
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()
与代码中的这一行相对应
var domainEventTypes = assembliesLoaded
.SelectMany(s => s.GetTypes())
.Where(x => typeof(IDomainEvent).IsAssignableFrom(x)
&& x.IsClass);
这意味着其中一个程序集包含一个类型,该类型包含对另一个未加载的程序集的引用。基于错误类型的名称,它似乎与城堡自动生成的类型有关。
您可以订阅静态的AppDomain.CurrentDomain.AssemblyResolve
和AppDomain.CurrentDomain.TypeResolve
事件,以更好地理解为什么不加载程序集并可能手动加载该程序集。有关更多信息,请参见Assembly.GetTypes() - ReflectionTypeLoadException。
在某些情况下,异常是“正常”的,可以用如下代码忽略:
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);
}
}
来自How to prevent ReflectionTypeLoadException when calling Assembly.GetTypes()的代码
您可以在这里使用它:
var domainEventTypes = assembliesLoaded
.SelectMany(s => s.GetLoadableTypes())
.Where(x => typeof(IDomainEvent).IsAssignableFrom(x)
&& x.IsClass);
答案 1 :(得分:1)
DynamicProxyGenAssembly2是由使用CastleProxy的模拟系统构建的临时程序集。 NSubstitute和Moq也存在一些类似的未解决问题,表明该问题是Castle.Core甚至.Net Framework中的竞争状况(有关更多详细信息,请参见TypeLoadException or BadImageFormatException under heavy multi-threaded proxy generation)
答案 2 :(得分:0)