XUnit Mock 没有像我期望的那样抛出异常

XUnit Mock not throwing exception like I would expect

我有以下使用 class 模拟的测试。当我尝试抛出异常时,它实际上从未抛出异常。它继续进行,就好像实际上正在调用该方法一样,我不确定为什么。

这是测试:

[Fact]
public async Task ReadResultSetShouldRetry()
{
    // Arrange
    _cosmosUtilWrapper.Setup(x => x.ReadCosmosResultSet<CosmosRepositoryTests>(It.IsAny<FeedIterator>(), It.IsAny<ILogger>(), It.IsAny<CancellationToken>()))
        .Throws(new Exception("It broke"));

    var cosmosReadPolicy = new CosmosReadPolicy();

    // Act
    await Assert.ThrowsAsync<Exception>(async () => await CosmosRepository.ReadCosmosResultSetWithRetry<CosmosRepositoryTests>(_mockFeedIterator.Object, _logger, cosmosReadPolicy, CancellationToken.None));
    // Assert
    _cosmosUtilWrapper.Verify(x => x.ReadCosmosResultSet<CosmosRepositoryTests>(_mockFeedIterator.Object, _logger, default));
}

这是它正在调用的方法,我将其包装在重试策略中:

public static async Task<List<T>> ReadCosmosResultSetWithRetry<T>(
    FeedIterator resultSet,
    ILogger logger,
    CosmosReadPolicy cosmosReadPolicy,
    CancellationToken cancellationToken = default)
    where T : class
{
    CosmosUtilWrapper utilWrapper = new CosmosUtilWrapper();

    var readCosmosResultSet = await cosmosReadPolicy.GetPolicy.ExecuteAsync(async () => await utilWrapper.ReadCosmosResultSet<T>(resultSet, logger, cancellationToken));

    return readCosmosResultSet;
}

这是 CosmosUtilWrapper,下面是实际的 Cosmos Util class:

public class CosmosUtilWrapper
{
    public virtual async Task<List<T>> ReadCosmosResultSet<T>(
        FeedIterator resultSet,
        ILogger logger,
        CancellationToken cancellationToken = default)
        where T : class
    {
        return await CosmosUtil.ReadCosmosResultSet<T>(resultSet, logger, cancellationToken);
    }
}

这是在上面 class 中返回的实际静态 Util 方法。不得不这样做,因为这个 class 是静态的 class 并且对它们进行单元测试不是很有趣。

public static async Task<List<T>> ReadCosmosResultSet<T>(
    FeedIterator resultSet,
    ILogger logger,
    CancellationToken cancellationToken = default)
    where T : class
{
    var foundDocuments = new List<T>();
    while (resultSet.HasMoreResults)
    {
        using ResponseMessage responseMessage = await resultSet.ReadNextAsync(cancellationToken);
        if (responseMessage.IsSuccessStatusCode)
        {
            using StreamReader streamReader = new StreamReader(responseMessage.Content);
            using JsonTextReader textReader = new JsonTextReader(streamReader);
            foundDocuments.AddRange((await JObject.LoadAsync(textReader, cancellationToken)).GetValue("Documents").ToObject<List<T>>());
        }
        else
        {
            throw new Exception($"Unable to read cosmos result set. Status code: {responseMessage.StatusCode}");
        }
    }

    return foundDocuments;
}

最后,这是我在 运行 测试

时收到的消息
  Message: 
    Assert.Throws() Failure
    Expected: typeof(System.Exception)
    Actual:   (No exception was thrown)

这是因为ReadResultSetShouldRetry()中的模型对象_cosmosUtilWrapper从未在Task<List<T>> ReadCosmosResultSetWithRetry<T>中使用过。

在方法 Task<List<T>> ReadCosmosResultSetWithRetry<T> 中,您初始化了一个新对象 CosmosUtilWrapper utilWrapper = new CosmosUtilWrapper();。所以,这个对象和上面的对象不一样。

您可以使用以下代码从模型对象中获取对象:_cosmosUtilWrapper.Object。从方法中删除静态时,在 class 的函数或构造函数中传递此对象。

例如:

public static async Task<List<T>> ReadCosmosResultSetWithRetry<T>(
    FeedIterator resultSet,
    ILogger logger,
    CosmosReadPolicy cosmosReadPolicy,
    CosmosUtilWrapper utilWrapper,
    CancellationToken cancellationToken = default)
    where T : class
{
    var readCosmosResultSet = await cosmosReadPolicy.GetPolicy.ExecuteAsync(async () => await utilWrapper.ReadCosmosResultSet<T>(resultSet, logger, cancellationToken));

    return readCosmosResultSet;
}

例如测试:

[Fact]
public async Task ReadResultSetShouldRetry()
{
    // Arrange
    _cosmosUtilWrapper.Setup(x => x.ReadCosmosResultSet<CosmosRepositoryTests>(It.IsAny<FeedIterator>(), It.IsAny<ILogger>(), It.IsAny<CancellationToken>()))
        .Throws(new Exception("It broke"));

    var cosmosReadPolicy = new CosmosReadPolicy();
    var utilWrapper = _cosmosUtilWrapper.Object;

    // Act
    await Assert.ThrowsAsync<Exception>(async () => await CosmosRepository.ReadCosmosResultSetWithRetry<CosmosRepositoryTests>(_mockFeedIterator.Object, _logger, cosmosReadPolicy, utilWrapper, CancellationToken.None));
    // Assert
    _cosmosUtilWrapper.Verify(x => x.ReadCosmosResultSet<CosmosRepositoryTests>(_mockFeedIterator.Object, _logger, default));
}