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