From 984829c36820d98646b2bd9b200479c9b3996486 Mon Sep 17 00:00:00 2001 From: GeWuYou <95328647+GeWuYou@users.noreply.github.com> Date: Mon, 16 Feb 2026 22:40:14 +0800 Subject: [PATCH] =?UTF-8?q?refactor(coroutine):=20=E7=A7=BB=E9=99=A4?= =?UTF-8?q?=E5=91=BD=E4=BB=A4=E7=AD=89=E5=BE=85=E4=BA=8B=E4=BB=B6=E5=8D=8F?= =?UTF-8?q?=E7=A8=8B=E6=89=A9=E5=B1=95=E5=B9=B6=E6=94=B9=E8=BF=9B=E6=B5=8B?= =?UTF-8?q?=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 从 MediatorCoroutineExtensions 中移除 SendCommandAndWaitEventCoroutine 相关代码 - 从 CommandCoroutineExtensionsTests 中移除对应的测试方法 - 更新测试用例验证逻辑,统一使用静态方法调用方式 - 添加新的 MediatorCoroutineExtensionsTests 测试类 - 修改测试方法名称以更准确反映测试行为 - 统一异常处理和参数验证的测试覆盖 --- .../CommandCoroutineExtensionsTests.cs | 68 +++++++---- .../MediatorCoroutineExtensionsTests.cs | 105 +++++++++++++++++ .../extensions/MediatorCoroutineExtensions.cs | 109 ------------------ 3 files changed, 151 insertions(+), 131 deletions(-) create mode 100644 GFramework.Core.Tests/coroutine/MediatorCoroutineExtensionsTests.cs diff --git a/GFramework.Core.Tests/coroutine/CommandCoroutineExtensionsTests.cs b/GFramework.Core.Tests/coroutine/CommandCoroutineExtensionsTests.cs index 0b71f94..2fc1f5c 100644 --- a/GFramework.Core.Tests/coroutine/CommandCoroutineExtensionsTests.cs +++ b/GFramework.Core.Tests/coroutine/CommandCoroutineExtensionsTests.cs @@ -133,10 +133,10 @@ public class CommandCoroutineExtensionsTests } /// - /// 验证SendCommandCoroutineWithErrorHandler应该处理null错误回调 + /// 验证SendCommandCoroutineWithErrorHandler在无错误处理程序时应该抛出异常 /// [Test] - public async Task SendCommandCoroutineWithErrorHandler_Should_Handle_Null_Error_Handler() + public void SendCommandCoroutineWithErrorHandler_Should_Throw_Exception_Without_Handler() { var command = new TestCommand(); var contextAware = new TestContextAware(); @@ -147,21 +147,19 @@ public class CommandCoroutineExtensionsTests .Setup(ctx => ctx.SendCommandAsync(It.IsAny())) .Returns(Task.FromException(expectedException)); - // 使用null作为错误处理程序 var coroutine = contextAware.SendCommandCoroutineWithErrorHandler(command); - // 迭代协程直到完成 - while (coroutine.MoveNext()) + // 迭代协程应该抛出异常 + Assert.Throws(() => { - if (coroutine.Current is WaitForTask waitForTask) + while (coroutine.MoveNext()) { - // 等待任务完成 - await Task.Delay(10); + if (coroutine.Current is WaitForTask waitForTask) + { + Task.Delay(10).Wait(); + } } - } - - // 应该不会抛出异常 - Assert.Pass(); + }); } /// @@ -194,7 +192,8 @@ public class CommandCoroutineExtensionsTests .Setup(ctx => ctx.SendCommandAsync(It.IsAny())) .Returns(Task.CompletedTask); - var coroutine = contextAware.SendCommandAndWaitEventCoroutine( + var coroutine = CommandCoroutineExtensions.SendCommandAndWaitEventCoroutine( + contextAware, command, ev => { @@ -250,7 +249,8 @@ public class CommandCoroutineExtensionsTests // 对于超时情况,我们期望抛出TimeoutException Assert.Throws(() => { - var coroutine = contextAware.SendCommandAndWaitEventCoroutine( + var coroutine = CommandCoroutineExtensions.SendCommandAndWaitEventCoroutine( + contextAware, command, null, 0.1f); // 0.1秒超时 @@ -292,7 +292,8 @@ public class CommandCoroutineExtensionsTests .Returns(Task.CompletedTask); // 使用null作为事件回调 - var coroutine = contextAware.SendCommandAndWaitEventCoroutine( + var coroutine = CommandCoroutineExtensions.SendCommandAndWaitEventCoroutine( + contextAware, command); // null回调 // 启动协程 @@ -334,7 +335,8 @@ public class CommandCoroutineExtensionsTests .Setup(ctx => ctx.SendCommandAsync(It.IsAny())) .Returns(Task.FromException(expectedException)); - var coroutine = contextAware.SendCommandAndWaitEventCoroutine( + var coroutine = CommandCoroutineExtensions.SendCommandAndWaitEventCoroutine( + contextAware, command, _ => { }); @@ -391,7 +393,8 @@ public class CommandCoroutineExtensionsTests .Setup(ctx => ctx.SendCommandAsync(It.IsAny())) .Returns(Task.CompletedTask); - var coroutine = contextAware.SendCommandAndWaitEventCoroutine( + var coroutine = CommandCoroutineExtensions.SendCommandAndWaitEventCoroutine( + contextAware, command, ev => { }); @@ -399,10 +402,30 @@ public class CommandCoroutineExtensionsTests } /// - /// 验证SendCommandAndWaitEventCoroutine应该在事件总线为null时处理异常 + /// 验证SendCommandAndWaitEventCoroutine应该在timeout小于0时抛出ArgumentOutOfRangeException /// [Test] - public void SendCommandAndWaitEventCoroutine_Should_Handle_Null_EventBus() + public void SendCommandAndWaitEventCoroutine_Should_Throw_ArgumentOutOfRange_When_Timeout_Negative() + { + var command = new TestCommand(); + var contextAware = new TestContextAware(); + + // 在创建协程时就应该抛出异常 + Assert.Throws(() => + { + CommandCoroutineExtensions.SendCommandAndWaitEventCoroutine( + contextAware, + command, + null, + -1.0f); + }); + } + + /// + /// 验证SendCommandAndWaitEventCoroutine应该在事件总线为null时抛出InvalidOperationException + /// + [Test] + public void SendCommandAndWaitEventCoroutine_Should_Throw_When_EventBus_Null() { var command = new TestCommand(); var contextAware = new TestContextAware(); @@ -413,11 +436,12 @@ public class CommandCoroutineExtensionsTests .Returns((IEventBus?)null); // 创建协程 - var coroutine = contextAware.SendCommandAndWaitEventCoroutine( + var coroutine = CommandCoroutineExtensions.SendCommandAndWaitEventCoroutine( + contextAware, command, ev => { }); - // 调用 MoveNext 时应该抛出异常,因为事件总线为null - Assert.Throws(() => coroutine.MoveNext()); + // 调用 MoveNext 时应该抛出 InvalidOperationException + Assert.Throws(() => coroutine.MoveNext()); } } \ No newline at end of file diff --git a/GFramework.Core.Tests/coroutine/MediatorCoroutineExtensionsTests.cs b/GFramework.Core.Tests/coroutine/MediatorCoroutineExtensionsTests.cs new file mode 100644 index 0000000..b23e887 --- /dev/null +++ b/GFramework.Core.Tests/coroutine/MediatorCoroutineExtensionsTests.cs @@ -0,0 +1,105 @@ +// Copyright (c) 2026 GeWuYou +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using GFramework.Core.Abstractions.architecture; +using GFramework.Core.Abstractions.coroutine; +using GFramework.Core.Abstractions.rule; +using GFramework.Core.coroutine.extensions; +using Mediator; +using Moq; +using NUnit.Framework; + +namespace GFramework.Core.Tests.coroutine; + +/// +/// MediatorCoroutineExtensions的单元测试类 +/// 测试Mediator模式与协程集成的扩展方法 +/// 注意:由于 Mediator 使用源生成器,本测试类主要验证接口和参数验证 +/// +[TestFixture] +public class MediatorCoroutineExtensionsTests +{ + /// + /// 测试用的简单命令类 + /// + private class TestCommand : IRequest + { + public string Data { get; set; } = string.Empty; + } + + /// + /// 测试用的简单事件类 + /// + private class TestEvent + { + public string Data { get; set; } = string.Empty; + } + + /// + /// 上下文感知基类的模拟实现 + /// + private class TestContextAware : IContextAware + { + public readonly Mock _mockContext = new(); + + public IArchitectureContext GetContext() + { + return _mockContext.Object; + } + + public void SetContext(IArchitectureContext context) + { + } + } + + /// + /// 验证SendCommandCoroutine应该返回IEnumerator + /// + [Test] + public void SendCommandCoroutine_Should_Return_IEnumerator_Of_YieldInstruction() + { + var command = new TestCommand { Data = "Test" }; + var contextAware = new TestContextAware(); + + // 创建 mediator 模拟 + var mediatorMock = new Mock(); + contextAware._mockContext + .Setup(ctx => ctx.GetService()) + .Returns(mediatorMock.Object); + + var coroutine = MediatorCoroutineExtensions.SendCommandCoroutine(contextAware, command); + + Assert.That(coroutine, Is.InstanceOf>()); + } + + /// + /// 验证SendCommandCoroutine应该在mediator为null时抛出NullReferenceException + /// + [Test] + public void SendCommandCoroutine_Should_Throw_When_Mediator_Null() + { + var command = new TestCommand { Data = "Test" }; + var contextAware = new TestContextAware(); + + // 设置上下文服务以返回null mediator + contextAware._mockContext + .Setup(ctx => ctx.GetService()) + .Returns((IMediator?)null); + + // 创建协程 + var coroutine = MediatorCoroutineExtensions.SendCommandCoroutine(contextAware, command); + + // 调用 MoveNext 时应该抛出 NullReferenceException + Assert.Throws(() => coroutine.MoveNext()); + } +} \ No newline at end of file diff --git a/GFramework.Core/coroutine/extensions/MediatorCoroutineExtensions.cs b/GFramework.Core/coroutine/extensions/MediatorCoroutineExtensions.cs index edbf02a..c808199 100644 --- a/GFramework.Core/coroutine/extensions/MediatorCoroutineExtensions.cs +++ b/GFramework.Core/coroutine/extensions/MediatorCoroutineExtensions.cs @@ -12,9 +12,7 @@ // limitations under the License. using GFramework.Core.Abstractions.coroutine; -using GFramework.Core.Abstractions.events; using GFramework.Core.Abstractions.rule; -using GFramework.Core.coroutine.instructions; using Mediator; namespace GFramework.Core.coroutine.extensions; @@ -53,111 +51,4 @@ public static class MediatorCoroutineExtensions else throw task.Exception!.InnerException ?? task.Exception; } - - // ... existing code ... - /// - /// 发送命令并等待特定事件的协程实现。 - /// - /// 命令类型 - /// 要等待的事件类型 - /// 上下文对象 - /// 要发送的命令 - /// 事件触发时的回调 - /// - /// 超时时间(秒): - /// - /// timeout < 0: 无效,将抛出 ArgumentOutOfRangeException - /// timeout == 0: 无超时,永久等待 - /// timeout > 0: 启用超时机制 - /// - /// - /// 当 timeout 小于 0 时抛出。 - public static IEnumerator SendCommandAndWaitEventCoroutine( - this IContextAware contextAware, - TCommand command, - Action? onEvent = null, - float timeout = 0f) - where TCommand : notnull - where TEvent : class - { - // 参数验证 - ValidateParameters(timeout); - - // 获取必要的服务 - var context = contextAware.GetContext(); - var mediator = context.GetService() - ?? throw new InvalidOperationException("IMediator not found."); - var eventBus = context.GetService() - ?? throw new InvalidOperationException("IEventBus not found."); - - // 执行协程逻辑 - return ExecuteSendCommandAndWaitEventCoroutine(mediator, eventBus, command, onEvent, timeout); - } - - /// - /// 验证方法参数的有效性。 - /// - /// 超时时间 - /// 当 timeout 小于 0 时抛出。 - private static void ValidateParameters(float timeout) - { - if (timeout < 0f) - throw new ArgumentOutOfRangeException( - nameof(timeout), - timeout, - "Timeout must be greater than or equal to 0."); - } - - /// - /// 执行发送命令并等待事件的协程逻辑。 - /// - /// 命令类型 - /// 事件类型 - /// 中介者服务 - /// 事件总线服务 - /// 要发送的命令 - /// 事件回调 - /// 超时时间 - /// 协程枚举器 - private static IEnumerator ExecuteSendCommandAndWaitEventCoroutine( - IMediator mediator, - IEventBus eventBus, - TCommand command, - Action? onEvent, - float timeout) - where TCommand : notnull - where TEvent : class - { - WaitForEvent? wait = null; - - try - { - wait = new WaitForEvent(eventBus); - - var task = mediator.Send(command).AsTask(); - yield return task.AsCoroutineInstruction(); - - if (timeout > 0f) - { - var timeoutWait = new WaitForEventWithTimeout(wait, timeout); - yield return timeoutWait; - - if (timeoutWait.IsTimeout) - throw new TimeoutException( - $"Wait for event {typeof(TEvent).Name} timeout."); - } - else - { - yield return wait; - } - - if (wait.EventData != null) - onEvent?.Invoke(wait.EventData); - } - finally - { - wait?.Dispose(); - } - } -// ... existing code ... } \ No newline at end of file