mirror of
https://github.com/GeWuYou/GFramework.git
synced 2026-05-07 00:39:00 +08:00
- 实现 ArchitectureContext 类的全面单元测试,覆盖构造函数、查询命令事件发送等功能 - 添加 MicrosoftDiContainer 依赖注入容器的完整测试,包括注册、获取、冻结等操作 - 创建 CqrsTestRuntime 测试基础设施,提供对 CQRS 处理器注册的受控访问 - 测试并发场景下的线程安全性,验证多线程环境下容器操作的正确性 - 实现优先级排序功能测试,确保服务按优先级正确排序和注册 - 添加各种边界条件测试,包括空参数异常处理和重复注册异常检测
135 lines
7.3 KiB
C#
135 lines
7.3 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Reflection;
|
|
using GFramework.Core.Abstractions.Cqrs;
|
|
using GFramework.Core.Abstractions.Ioc;
|
|
using GFramework.Core.Abstractions.Logging;
|
|
using GFramework.Core.Architectures;
|
|
using GFramework.Core.Ioc;
|
|
using GFramework.Core.Logging;
|
|
using GFramework.Cqrs.Abstractions.Cqrs;
|
|
|
|
namespace GFramework.Tests.Common;
|
|
|
|
/// <summary>
|
|
/// 为测试项目提供对 CQRS 处理器真实注册入口的受控访问。
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// 该测试基础设施位于独立模块中,避免多个测试项目复制同一份反射绑定与默认 runtime 接线逻辑。
|
|
/// 测试应通过该入口驱动注册流程,而不是各自维护一份实现细节副本。
|
|
/// </remarks>
|
|
public static class CqrsTestRuntime
|
|
{
|
|
private static readonly Type CqrsHandlerRegistrarType = typeof(ArchitectureContext).Assembly
|
|
.GetType(
|
|
"GFramework.Core.Cqrs.Internal.CqrsHandlerRegistrar",
|
|
throwOnError: true)!;
|
|
|
|
private static readonly MethodInfo RegisterHandlersMethod = CqrsHandlerRegistrarType
|
|
.GetMethod(
|
|
"RegisterHandlers",
|
|
BindingFlags.Public | BindingFlags.NonPublic |
|
|
BindingFlags.Static,
|
|
binder: null,
|
|
[
|
|
typeof(IIocContainer),
|
|
typeof(IEnumerable<Assembly>),
|
|
typeof(ILogger)
|
|
],
|
|
modifiers: null)
|
|
?? throw new InvalidOperationException(
|
|
"Failed to locate CqrsHandlerRegistrar.RegisterHandlers.");
|
|
|
|
private static readonly Type CqrsDispatcherType = typeof(ArchitectureContext).Assembly
|
|
.GetType(
|
|
"GFramework.Core.Cqrs.Internal.CqrsDispatcher",
|
|
throwOnError: true)!;
|
|
|
|
private static readonly ConstructorInfo CqrsDispatcherConstructor = CqrsDispatcherType.GetConstructor(
|
|
BindingFlags.Instance |
|
|
BindingFlags.Public |
|
|
BindingFlags.NonPublic,
|
|
binder: null,
|
|
[
|
|
typeof(IIocContainer),
|
|
typeof(ILogger)
|
|
],
|
|
modifiers: null)
|
|
?? throw new InvalidOperationException(
|
|
"Failed to locate CqrsDispatcher constructor.");
|
|
|
|
private static readonly Type DefaultCqrsHandlerRegistrarType = typeof(ArchitectureContext).Assembly
|
|
.GetType(
|
|
"GFramework.Core.Cqrs.Internal.DefaultCqrsHandlerRegistrar",
|
|
throwOnError: true)!;
|
|
|
|
private static readonly ConstructorInfo DefaultCqrsHandlerRegistrarConstructor =
|
|
DefaultCqrsHandlerRegistrarType.GetConstructor(
|
|
BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic,
|
|
binder: null,
|
|
[
|
|
typeof(IIocContainer),
|
|
typeof(ILogger)
|
|
],
|
|
modifiers: null)
|
|
?? throw new InvalidOperationException(
|
|
"Failed to locate DefaultCqrsHandlerRegistrar constructor.");
|
|
|
|
/// <summary>
|
|
/// 为裸测试容器补齐默认 CQRS runtime seam。
|
|
/// </summary>
|
|
/// <param name="container">目标测试容器。</param>
|
|
/// <exception cref="ArgumentNullException"><paramref name="container" /> 为 <see langword="null" />。</exception>
|
|
/// <exception cref="TargetInvocationException">反射调用底层 CQRS runtime 或注册器构造函数失败时抛出。</exception>
|
|
/// <remarks>
|
|
/// 这使仅使用 <see cref="MicrosoftDiContainer" /> 的测试环境也能观察与生产路径一致的 runtime 行为,
|
|
/// 而无需完整启动服务模块管理器。
|
|
/// 该方法按服务类型执行幂等注册,只会补齐当前容器中尚未接线的 CQRS 基础设施。
|
|
/// </remarks>
|
|
public static void RegisterInfrastructure(MicrosoftDiContainer container)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(container);
|
|
|
|
if (container.Get<ICqrsRuntime>() is null)
|
|
{
|
|
var runtimeLogger = LoggerFactoryResolver.Provider.CreateLogger(CqrsDispatcherType.Name);
|
|
var runtime = (ICqrsRuntime)CqrsDispatcherConstructor.Invoke([container, runtimeLogger]);
|
|
container.Register<ICqrsRuntime>(runtime);
|
|
}
|
|
|
|
if (container.Get<ICqrsHandlerRegistrar>() is null)
|
|
{
|
|
var registrarLogger = LoggerFactoryResolver.Provider.CreateLogger(DefaultCqrsHandlerRegistrarType.Name);
|
|
var registrar =
|
|
(ICqrsHandlerRegistrar)DefaultCqrsHandlerRegistrarConstructor.Invoke([container, registrarLogger]);
|
|
container.Register<ICqrsHandlerRegistrar>(registrar);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 通过与生产代码一致的注册入口扫描并注册指定程序集中的 CQRS 处理器。
|
|
/// </summary>
|
|
/// <param name="container">承载处理器映射的测试容器。</param>
|
|
/// <param name="assemblies">要扫描的程序集集合。</param>
|
|
/// <exception cref="ArgumentNullException">
|
|
/// <paramref name="container" /> 或 <paramref name="assemblies" /> 为 <see langword="null" />。
|
|
/// </exception>
|
|
/// <exception cref="TargetInvocationException">反射调用底层 CQRS 处理器注册入口失败时抛出。</exception>
|
|
/// <remarks>
|
|
/// 该入口会自动调用 <see cref="RegisterInfrastructure" />,因此测试通常无需预先手动接线 CQRS 基础设施。
|
|
/// 程序集去重与空元素过滤由生产注册入口统一处理,避免测试辅助层复制相同筛选逻辑。
|
|
/// </remarks>
|
|
public static void RegisterHandlers(MicrosoftDiContainer container, params Assembly[] assemblies)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(container);
|
|
ArgumentNullException.ThrowIfNull(assemblies);
|
|
|
|
RegisterInfrastructure(container);
|
|
|
|
var logger = LoggerFactoryResolver.Provider.CreateLogger(nameof(CqrsTestRuntime));
|
|
RegisterHandlersMethod.Invoke(
|
|
null,
|
|
[container, assemblies, logger]);
|
|
}
|
|
}
|