mirror of
https://github.com/GeWuYou/GFramework.git
synced 2026-03-22 10:34:30 +08:00
feat(architecture): 添加架构上下文提供者和相关测试
- 新增 IArchitectureContextProvider 接口定义 - 实现 GameContextProvider 类提供上下文获取功能 - 添加 GameContext 静态类用于获取架构上下文 - 创建 ContextProviderTests 测试上下文提供者功能 - 实现 RegistryInitializationHookBase 抽象基类的完整测试 - 修改 IArchitectureContext.GetUtility 方法为虚拟方法以支持重写
This commit is contained in:
parent
0ed8edf015
commit
3ecce110ed
157
GFramework.Core.Tests/architecture/ContextProviderTests.cs
Normal file
157
GFramework.Core.Tests/architecture/ContextProviderTests.cs
Normal file
@ -0,0 +1,157 @@
|
||||
using GFramework.Core.Abstractions.architecture;
|
||||
using GFramework.Core.architecture;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace GFramework.Core.Tests.architecture;
|
||||
|
||||
/// <summary>
|
||||
/// ContextProvider 相关类的单元测试
|
||||
/// 测试内容包括:
|
||||
/// - GameContextProvider 获取第一个架构上下文
|
||||
/// - GameContextProvider 尝试获取指定类型的上下文
|
||||
/// - ScopedContextProvider 获取绑定的上下文
|
||||
/// - ScopedContextProvider 尝试获取指定类型的上下文
|
||||
/// - ScopedContextProvider 类型不匹配时返回 false
|
||||
/// </summary>
|
||||
[TestFixture]
|
||||
public class ContextProviderTests
|
||||
{
|
||||
/// <summary>
|
||||
/// 测试初始化方法,在每个测试方法执行前清空 GameContext
|
||||
/// </summary>
|
||||
[SetUp]
|
||||
public void SetUp()
|
||||
{
|
||||
GameContext.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 测试清理方法,在每个测试方法执行后清空 GameContext
|
||||
/// </summary>
|
||||
[TearDown]
|
||||
public void TearDown()
|
||||
{
|
||||
GameContext.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 测试 GameContextProvider 是否能正确获取第一个架构上下文
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void GameContextProvider_GetContext_Should_Return_First_Context()
|
||||
{
|
||||
var context = new TestArchitectureContext();
|
||||
GameContext.Bind(typeof(TestArchitecture), context);
|
||||
|
||||
var provider = new GameContextProvider();
|
||||
var result = provider.GetContext();
|
||||
|
||||
Assert.That(result, Is.SameAs(context));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 测试 GameContextProvider 在没有上下文时是否抛出异常
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void GameContextProvider_GetContext_Should_Throw_When_Empty()
|
||||
{
|
||||
var provider = new GameContextProvider();
|
||||
|
||||
Assert.Throws<InvalidOperationException>(() => provider.GetContext());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 测试 GameContextProvider 的 TryGetContext 方法在找到上下文时返回 true
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void GameContextProvider_TryGetContext_Should_Return_True_When_Found()
|
||||
{
|
||||
var context = new TestArchitectureContext();
|
||||
GameContext.Bind(typeof(TestArchitectureContext), context);
|
||||
|
||||
var provider = new GameContextProvider();
|
||||
var result = provider.TryGetContext<TestArchitectureContext>(out var foundContext);
|
||||
|
||||
Assert.That(result, Is.True);
|
||||
Assert.That(foundContext, Is.SameAs(context));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 测试 GameContextProvider 的 TryGetContext 方法在未找到上下文时返回 false
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void GameContextProvider_TryGetContext_Should_Return_False_When_Not_Found()
|
||||
{
|
||||
var provider = new GameContextProvider();
|
||||
var result = provider.TryGetContext<TestArchitectureContext>(out var foundContext);
|
||||
|
||||
Assert.That(result, Is.False);
|
||||
Assert.That(foundContext, Is.Null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 测试 ScopedContextProvider 是否能正确返回绑定的上下文
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void ScopedContextProvider_GetContext_Should_Return_Bound_Context()
|
||||
{
|
||||
var context = new TestArchitectureContext();
|
||||
var provider = new ScopedContextProvider(context);
|
||||
|
||||
var result = provider.GetContext();
|
||||
|
||||
Assert.That(result, Is.SameAs(context));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 测试 ScopedContextProvider 的 TryGetContext 方法在类型匹配时返回 true
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void ScopedContextProvider_TryGetContext_Should_Return_True_When_Type_Matches()
|
||||
{
|
||||
var context = new TestArchitectureContext();
|
||||
var provider = new ScopedContextProvider(context);
|
||||
|
||||
var result = provider.TryGetContext<TestArchitectureContext>(out var foundContext);
|
||||
|
||||
Assert.That(result, Is.True);
|
||||
Assert.That(foundContext, Is.SameAs(context));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 测试 ScopedContextProvider 的 TryGetContext 方法在类型不匹配时返回 false
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void ScopedContextProvider_TryGetContext_Should_Return_False_When_Type_Does_Not_Match()
|
||||
{
|
||||
var context = new TestArchitectureContext();
|
||||
var provider = new ScopedContextProvider(context);
|
||||
|
||||
var result = provider.TryGetContext<AnotherTestArchitectureContext>(out var foundContext);
|
||||
|
||||
Assert.That(result, Is.False);
|
||||
Assert.That(foundContext, Is.Null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 测试 ScopedContextProvider 的 TryGetContext 方法支持接口类型查询
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void ScopedContextProvider_TryGetContext_Should_Support_Interface_Type()
|
||||
{
|
||||
var context = new TestArchitectureContext();
|
||||
var provider = new ScopedContextProvider(context);
|
||||
|
||||
var result = provider.TryGetContext<IArchitectureContext>(out var foundContext);
|
||||
|
||||
Assert.That(result, Is.True);
|
||||
Assert.That(foundContext, Is.SameAs(context));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 另一个测试用的架构上下文类,用于测试类型不匹配的情况
|
||||
/// </summary>
|
||||
public class AnotherTestArchitectureContext : TestArchitectureContext
|
||||
{
|
||||
}
|
||||
@ -294,7 +294,7 @@ public class TestArchitectureContext : IArchitectureContext
|
||||
/// </summary>
|
||||
/// <typeparam name="TUtility">工具类型</typeparam>
|
||||
/// <returns>工具实例或null</returns>
|
||||
public TUtility? GetUtility<TUtility>() where TUtility : class, IUtility
|
||||
public virtual TUtility? GetUtility<TUtility>() where TUtility : class, IUtility
|
||||
{
|
||||
return _container.Get<TUtility>();
|
||||
}
|
||||
|
||||
@ -0,0 +1,367 @@
|
||||
using GFramework.Core.Abstractions.architecture;
|
||||
using GFramework.Core.Abstractions.enums;
|
||||
using GFramework.Core.Abstractions.lifecycle;
|
||||
using GFramework.Core.Abstractions.model;
|
||||
using GFramework.Core.Abstractions.system;
|
||||
using GFramework.Core.Abstractions.utility;
|
||||
using GFramework.Core.architecture;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace GFramework.Core.Tests.architecture;
|
||||
|
||||
/// <summary>
|
||||
/// RegistryInitializationHookBase 抽象基类的单元测试
|
||||
/// 测试内容包括:
|
||||
/// - 在目标阶段正确触发配置注册
|
||||
/// - 在非目标阶段不触发配置注册
|
||||
/// - 正确遍历所有配置项
|
||||
/// - 注册表不存在时不抛出异常
|
||||
/// - 支持自定义目标阶段
|
||||
/// </summary>
|
||||
[TestFixture]
|
||||
public class RegistryInitializationHookBaseTests
|
||||
{
|
||||
/// <summary>
|
||||
/// 测试在目标阶段时是否正确触发配置注册
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void OnPhase_Should_Register_Configs_At_Target_Phase()
|
||||
{
|
||||
var registry = new TestRegistry();
|
||||
var configs = new[] { "config1", "config2", "config3" };
|
||||
var hook = new TestRegistryInitializationHook(configs);
|
||||
var architecture = new TestArchitectureWithRegistry(registry);
|
||||
|
||||
hook.OnPhase(ArchitecturePhase.AfterSystemInit, architecture);
|
||||
|
||||
Assert.That(registry.RegisteredConfigs.Count, Is.EqualTo(3));
|
||||
Assert.That(registry.RegisteredConfigs, Is.EquivalentTo(configs));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 测试在非目标阶段时不触发配置注册
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void OnPhase_Should_Not_Register_Configs_At_Wrong_Phase()
|
||||
{
|
||||
var registry = new TestRegistry();
|
||||
var configs = new[] { "config1", "config2" };
|
||||
var hook = new TestRegistryInitializationHook(configs);
|
||||
var architecture = new TestArchitectureWithRegistry(registry);
|
||||
|
||||
hook.OnPhase(ArchitecturePhase.BeforeSystemInit, architecture);
|
||||
|
||||
Assert.That(registry.RegisteredConfigs.Count, Is.EqualTo(0));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 测试支持自定义目标阶段
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void OnPhase_Should_Support_Custom_Target_Phase()
|
||||
{
|
||||
var registry = new TestRegistry();
|
||||
var configs = new[] { "config1" };
|
||||
var hook = new TestRegistryInitializationHook(configs, ArchitecturePhase.AfterModelInit);
|
||||
var architecture = new TestArchitectureWithRegistry(registry);
|
||||
|
||||
hook.OnPhase(ArchitecturePhase.AfterModelInit, architecture);
|
||||
|
||||
Assert.That(registry.RegisteredConfigs.Count, Is.EqualTo(1));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 测试当注册表不存在时不抛出异常
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void OnPhase_Should_Not_Throw_When_Registry_Not_Found()
|
||||
{
|
||||
var configs = new[] { "config1" };
|
||||
var hook = new TestRegistryInitializationHook(configs);
|
||||
var architecture = new TestArchitectureWithoutRegistry();
|
||||
|
||||
Assert.DoesNotThrow(() => hook.OnPhase(ArchitecturePhase.AfterSystemInit, architecture));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 测试空配置集合不会导致错误
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void OnPhase_Should_Handle_Empty_Configs()
|
||||
{
|
||||
var registry = new TestRegistry();
|
||||
var configs = Array.Empty<string>();
|
||||
var hook = new TestRegistryInitializationHook(configs);
|
||||
var architecture = new TestArchitectureWithRegistry(registry);
|
||||
|
||||
hook.OnPhase(ArchitecturePhase.AfterSystemInit, architecture);
|
||||
|
||||
Assert.That(registry.RegisteredConfigs.Count, Is.EqualTo(0));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 测试多次调用同一阶段会重复注册
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void OnPhase_Should_Register_Multiple_Times_If_Called_Multiple_Times()
|
||||
{
|
||||
var registry = new TestRegistry();
|
||||
var configs = new[] { "config1" };
|
||||
var hook = new TestRegistryInitializationHook(configs);
|
||||
var architecture = new TestArchitectureWithRegistry(registry);
|
||||
|
||||
hook.OnPhase(ArchitecturePhase.AfterSystemInit, architecture);
|
||||
hook.OnPhase(ArchitecturePhase.AfterSystemInit, architecture);
|
||||
|
||||
Assert.That(registry.RegisteredConfigs.Count, Is.EqualTo(2));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 测试用的注册表初始化钩子实现
|
||||
/// </summary>
|
||||
public class TestRegistryInitializationHook : RegistryInitializationHookBase<TestRegistry, string>
|
||||
{
|
||||
public TestRegistryInitializationHook(
|
||||
IEnumerable<string> configs,
|
||||
ArchitecturePhase targetPhase = ArchitecturePhase.AfterSystemInit)
|
||||
: base(configs, targetPhase)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void RegisterConfig(TestRegistry registry, string config)
|
||||
{
|
||||
registry.Register(config);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 测试用的注册表类
|
||||
/// </summary>
|
||||
public class TestRegistry : IUtility
|
||||
{
|
||||
public List<string> RegisteredConfigs { get; } = new();
|
||||
|
||||
public void Register(string config)
|
||||
{
|
||||
RegisteredConfigs.Add(config);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 测试用的架构类(包含注册表)
|
||||
/// </summary>
|
||||
public class TestArchitectureWithRegistry : IArchitecture
|
||||
{
|
||||
private readonly TestRegistry _registry;
|
||||
|
||||
public TestArchitectureWithRegistry(TestRegistry registry)
|
||||
{
|
||||
_registry = registry;
|
||||
Context = new TestArchitectureContextWithRegistry(registry);
|
||||
}
|
||||
|
||||
public Action<IServiceCollection>? Configurator { get; }
|
||||
|
||||
public IArchitectureContext Context { get; }
|
||||
Action<IServiceCollection>? IArchitecture.Configurator => Configurator;
|
||||
|
||||
T IArchitecture.RegisterSystem<T>(T system)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
T IArchitecture.RegisterModel<T>(T model)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
T IArchitecture.RegisterUtility<T>(T utility)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void RegisterMediatorBehavior<TBehavior>() where TBehavior : class
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IArchitectureModule InstallModule(IArchitectureModule module)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
IArchitectureLifecycleHook IArchitecture.RegisterLifecycleHook(IArchitectureLifecycleHook hook)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
Task IArchitecture.WaitUntilReadyAsync()
|
||||
{
|
||||
return WaitUntilReadyAsync();
|
||||
}
|
||||
|
||||
public void RegisterUtility<T>(Action<T>? onCreated = default(Action<T>?)) where T : class, IUtility
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void RegisterModel<T>(Action<T>? onCreated = default(Action<T>?)) where T : class, IModel
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void RegisterSystem<T>(Action<T>? onCreated = default(Action<T>?)) where T : class, ISystem
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
}
|
||||
|
||||
public void Destroy()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
Task IAsyncInitializable.InitializeAsync()
|
||||
{
|
||||
return InitializeAsync();
|
||||
}
|
||||
|
||||
ValueTask IAsyncDestroyable.DestroyAsync()
|
||||
{
|
||||
return DestroyAsync();
|
||||
}
|
||||
|
||||
public Task WaitUntilReadyAsync()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void RegisterLifecycleHook(IArchitectureLifecycleHook hook)
|
||||
{
|
||||
}
|
||||
|
||||
public Task InitializeAsync()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public ValueTask DestroyAsync()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 测试用的架构上下文类(包含注册表)
|
||||
/// </summary>
|
||||
public class TestArchitectureContextWithRegistry : TestArchitectureContext
|
||||
{
|
||||
private readonly TestRegistry _registry;
|
||||
|
||||
public TestArchitectureContextWithRegistry(TestRegistry registry)
|
||||
{
|
||||
_registry = registry;
|
||||
}
|
||||
|
||||
public override TUtility GetUtility<TUtility>()
|
||||
{
|
||||
if (typeof(TUtility) == typeof(TestRegistry))
|
||||
{
|
||||
return _registry as TUtility;
|
||||
}
|
||||
|
||||
return base.GetUtility<TUtility>();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 测试用的架构类(不包含注册表)
|
||||
/// </summary>
|
||||
public class TestArchitectureWithoutRegistry : IArchitecture
|
||||
{
|
||||
public TestArchitectureWithoutRegistry()
|
||||
{
|
||||
Context = new TestArchitectureContext();
|
||||
}
|
||||
|
||||
public IArchitectureContext Context { get; }
|
||||
public Action<IServiceCollection>? Configurator { get; }
|
||||
|
||||
T IArchitecture.RegisterSystem<T>(T system)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
T IArchitecture.RegisterModel<T>(T model)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
T IArchitecture.RegisterUtility<T>(T utility)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void RegisterMediatorBehavior<TBehavior>() where TBehavior : class
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IArchitectureModule InstallModule(IArchitectureModule module)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
IArchitectureLifecycleHook IArchitecture.RegisterLifecycleHook(IArchitectureLifecycleHook hook)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task WaitUntilReadyAsync()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void RegisterUtility<T>(Action<T>? onCreated = default(Action<T>?)) where T : class, IUtility
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void RegisterModel<T>(Action<T>? onCreated = default(Action<T>?)) where T : class, IModel
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void RegisterSystem<T>(Action<T>? onCreated = default(Action<T>?)) where T : class, ISystem
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
}
|
||||
|
||||
public Task InitializeAsync()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public ValueTask DestroyAsync()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void Destroy()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void RegisterLifecycleHook(IArchitectureLifecycleHook hook)
|
||||
{
|
||||
}
|
||||
}
|
||||
@ -44,6 +44,32 @@ public class ContextAwareGeneratorSnapshotTests
|
||||
namespace GFramework.Core.Abstractions.architecture
|
||||
{
|
||||
public interface IArchitectureContext { }
|
||||
|
||||
public interface IArchitectureContextProvider
|
||||
{
|
||||
IArchitectureContext GetContext();
|
||||
bool TryGetContext<T>(out T? context) where T : class, IArchitectureContext;
|
||||
}
|
||||
}
|
||||
|
||||
namespace GFramework.Core.architecture
|
||||
{
|
||||
using GFramework.Core.Abstractions.architecture;
|
||||
|
||||
public sealed class GameContextProvider : IArchitectureContextProvider
|
||||
{
|
||||
public IArchitectureContext GetContext() => null;
|
||||
public bool TryGetContext<T>(out T? context) where T : class, IArchitectureContext
|
||||
{
|
||||
context = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static class GameContext
|
||||
{
|
||||
public static IArchitectureContext GetFirstArchitectureContext() => null;
|
||||
}
|
||||
}
|
||||
|
||||
namespace TestApp
|
||||
@ -56,21 +82,6 @@ public class ContextAwareGeneratorSnapshotTests
|
||||
{
|
||||
}
|
||||
}
|
||||
namespace GFramework.Core.architecture
|
||||
{
|
||||
using GFramework.Core.Abstractions.architecture;
|
||||
public static class GameContext{
|
||||
/// <summary>
|
||||
/// 获取字典中的第一个架构上下文
|
||||
/// </summary>
|
||||
/// <returns>返回字典中的第一个架构上下文实例</returns>
|
||||
/// <exception cref="InvalidOperationException">当字典为空时抛出</exception>
|
||||
public static IArchitectureContext GetFirstArchitectureContext()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
// 执行生成器快照测试,将生成的代码与预期快照进行比较
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user