You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Moq验证队列创建逻辑中被捕获的特定聚合异常

如何用Moq验证CreateQueueIfNotExists捕获MessagingEntityAlreadyExistsException?

我有一个按需创建队列的方法CreateQueueIfNotExists:队列已存在则返回false,创建成功则返回true。现在需要测试这样的场景:调用QueueExists检查时队列不存在,但在调用CreateQueueAsync前被其他进程创建(也就是CreateQueueAsync会抛出MessagingEntityAlreadyExistsException,被方法内部捕获)。

产品代码

internal bool QueueExists(string queueName) { 
    var allQueues = ManagementClient.GetQueuesAsync().Result; 
    if (!allQueues.Any(q => q.Path.Equals(queueName))) { 
        return false; 
    } 
    return true; 
}

public bool CreateQueueIfNotExists(string queueName, bool requiresSession) { 
    if (!QueueExists(queueName)) { 
        try { 
            ManagementClient.CreateQueueAsync(queueDescription).Wait(); 
            return true; 
        } catch (AggregateException ae) { 
            Exception e = ae.GetBaseException(); 
            // 如果队列已存在则无需额外操作
            if (e is MessagingEntityAlreadyExistsException) { 
                return false; 
            } else { 
                ExceptionDispatchInfo.Throw(e); 
            } 
        } 
    } else { 
        return false; 
    } 
}

现有NUnit测试

[Test] 
public void QueueCreation_MessagingEntityAlreadyExistsException_IsCatched() { 
    // Given 
    var managementClientMock = new Mock<ManagementClient>(connectionString) { CallBase = true }; 
    managementClientMock.Setup(m => m.GetQueuesAsync(It.IsAny<int>(), It.IsAny<int>(), It.IsAny<System.Threading.CancellationToken>())) 
        .Returns(Task.FromResult<IList<QueueDescription>>(new List<QueueDescription>())); 
    QueueFactory queueFactory = new QueueFactory(managementClientMock.Object); 

    Assert.That(queueFactory.CreateQueueIfNotExists(alreadyExistsQueue, true), Is.True, "第一次队列创建未返回true"); 
    Assert.That(queueFactory.CreateQueueIfNotExists(alreadyExistsQueue, true), Is.False, "第二次队列创建未返回false"); 
    managementClientMock.Verify(m => m.CreateQueueAsync(It.IsAny<QueueDescription>(), It.IsAny<System.Threading.CancellationToken>()), Times.Exactly(2)); 
}

当前测试能验证CreateQueueAsync的调用次数,但我需要验证第二次调用时,CreateQueueAsync抛出的包含MessagingEntityAlreadyExistsException的AggregateException被方法正确捕获。由于异常已被业务方法内部处理,ExpectedException和Assert.Throws都不适用,请问能否用Moq实现这个验证?


解决方案:用Moq模拟抛出指定异常并验证行为

当然可以!核心思路是让Moq在第二次调用CreateQueueAsync时返回一个已失败的Task,内部包含MessagingEntityAlreadyExistsException,然后验证你的方法是否正确捕获这个异常并返回false,同时没有向外抛出异常。

修改后的测试代码如下:

[Test]
public void QueueCreation_MessagingEntityAlreadyExistsException_IsCatched()
{
    // Given
    var connectionString = "your-test-connection-string";
    var alreadyExistsQueue = "test-queue";
    var managementClientMock = new Mock<ManagementClient>(connectionString) { CallBase = true };

    // 模拟GetQueuesAsync始终返回空列表,让QueueExists认为队列不存在
    managementClientMock.Setup(m => m.GetQueuesAsync(It.IsAny<int>(), It.IsAny<int>(), It.IsAny<CancellationToken>()))
        .Returns(Task.FromResult<IList<QueueDescription>>(new List<QueueDescription>()));

    // 用SetupSequence设置CreateQueueAsync的两次调用行为:
    // 第一次调用:成功完成(模拟队列创建成功)
    // 第二次调用:返回包含MessagingEntityAlreadyExistsException的失败Task
    var expectedException = new MessagingEntityAlreadyExistsException("队列已存在");
    managementClientMock.SetupSequence(m => m.CreateQueueAsync(It.IsAny<QueueDescription>(), It.IsAny<CancellationToken>()))
        .Returns(Task.CompletedTask)
        .Returns(Task.FromException<object>(new AggregateException(expectedException)));

    QueueFactory queueFactory = new QueueFactory(managementClientMock.Object);

    // When & Then
    // 第一次调用:应该返回true,因为队列创建成功
    Assert.That(queueFactory.CreateQueueIfNotExists(alreadyExistsQueue, true), Is.True, "第一次队列创建未返回true");
    
    // 第二次调用:应该返回false,因为CreateQueueAsync抛出的异常被捕获
    // 同时这里不会向外抛出异常,说明异常被方法正确处理了
    Assert.That(() => queueFactory.CreateQueueIfNotExists(alreadyExistsQueue, true), Throws.Nothing, "第二次调用意外抛出了异常");
    Assert.That(queueFactory.CreateQueueIfNotExists(alreadyExistsQueue, true), Is.False, "第二次队列创建未返回false");

    // 验证CreateQueueAsync确实被调用了两次
    managementClientMock.Verify(m => m.CreateQueueAsync(It.IsAny<QueueDescription>(), It.IsAny<CancellationToken>()), Times.Exactly(2));
}

关键细节说明:

  1. SetupSequence的使用:通过SetupSequence可以为同一个方法的多次调用设置不同的行为,完美匹配我们“第一次创建成功,第二次抛出异常”的测试场景。
  2. 模拟失败的Task:Task.FromException<object>(new AggregateException(expectedException))会创建一个已失败的Task,当调用Wait()时会抛出AggregateException,和真实场景一致。
  3. 验证异常被捕获:用Assert.That(() => ..., Throws.Nothing)来确认第二次调用方法时没有向外抛出异常,说明内部的异常捕获逻辑生效了。
  4. 返回值验证:第二次调用返回false,符合方法的预期行为。

这样就能完整验证你的方法在并发场景下的异常处理逻辑啦!

内容的提问来源于stack exchange,提问作者fota666

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.06 13:38:21