无法使用 Moq 设置模拟的 Redis IDatabase

问题描述 投票:0回答:1

这是我对程序中缓存的测试:

[Fact]
public async Task Cache_Works_Correctly()
{
    const string testkey = "69";
    const string testvalue = "Nice";

    var mockDatabase = new Mock<IDatabase>();
  
    mockDatabase.Setup<string>(_ => _.StringGet(CacheConstants.NamePrefix + testkey, CommandFlags.None)).Returns(testvalue);

    var mockMultiplexer = new Mock<IConnectionMultiplexer>();
    mockMultiplexer.Setup(_ => _.IsConnected).Returns(false);

    mockMultiplexer
        .Setup(_ => _.GetDatabase(It.IsAny<int>(), It.IsAny<object>()))
        .Returns(mockDatabase.Object);

    var cache = new CacheStore(mockMultiplexer.Object.GetDatabase(), new Mock<ILogger<CacheStore>>().Object);

 
    Assert.Equal(testvalue, await cache.GetValueAsync<string>(testkey));
}

问题出现在这一行:

 mockDatabase.Setup<string>(_ => _.StringGet(CacheConstants.NamePrefix + testkey, CommandFlags.None)).Returns(testvalue);

当我运行测试时,我收到参数异常:

Message: 
System.ArgumentException : Unsupported expression: (string)_.StringGet((RedisKey)"SummaryAPI_69", CommandFlags.None)

  Stack Trace: 
ExpressionExtensions.Split(LambdaExpression expression, Boolean allowNonOverridableLastProperty) line 159
Mock.SetupRecursive[TSetup](Mock mock, LambdaExpression expression, Func`4 setupLast, Boolean allowNonOverridableLastProperty) line 645
Mock.Setup(Mock mock, LambdaExpression expression, Condition condition) line 500
Mock`1.Setup[TResult](Expression`1 expression) line 452
UniversalCacheTests.Cache_Works_Correctly() line 23
--- End of stack trace from previous location ---

Moq 似乎无法设置 IDatabase 的函数 StringGet(),但它可以与 ToString() 等配合使用。有什么办法可以做我想做的事吗?

这是我的 CacheStore.GetValueAsync 函数,我想通过模拟 IDatabase 来测试它:

public async Task<TValue?> GetValueAsync<TValue>(string key)
{
    key = CacheConstants.NamePrefix + key;
    this._logger.LogInformation($"Getting {key} from cache.");

    var jsonData = await Task.Run(() => this._database.StringGet(key));

    if (jsonData == RedisValue.Null)
    {
        return default;
    }

    return JsonSerializer.Deserialize<TValue>(jsonData!);
}

我尝试检查是否可以设置 IDatabase 的其他功能,并且成功了!看来 Moq 无法使用该特定功能。查看代码,问题出现在 Moq.Split() 函数中。据说是这样的

 Splits an expression such as m => m.A.B(x).C[y] = z into a chain of parts
          that can be set up one at a time:
            m => m.A
            ... => ....B(x)
            ... => ....C
            ... => ...[y] = z

为什么 IDatabase.StringGet 有问题?我不知道

代码如下:

  internal static Stack<MethodExpectation> Split(this LambdaExpression expression, bool allowNonOverridableLastProperty = false)
  {
      Debug.Assert(expression != null);

      var parts = new Stack<MethodExpectation>();

      Expression remainder = expression.Body;
      while (CanSplit(remainder))
      {
          Split(remainder, out remainder, out var part, allowNonOverridableLastProperty: allowNonOverridableLastProperty && parts.Count == 0);
          parts.Push(part);
      }

      if (parts.Count > 0 && remainder is ParameterExpression)
      {
          return parts;
      }
      else
      {
          throw new ArgumentException(
              string.Format(
                  CultureInfo.CurrentCulture,
                  Resources.UnsupportedExpression,
                  remainder.ToStringFixed()));
      }
c# unit-testing moq stackexchange.redis
1个回答
0
投票

您可以尝试以不同的方式设置模拟以匹配更广泛的参数,并查看问题出在哪里。

所以改变这个

mockDatabase
    .Setup<string>(_ => _.StringGet(CacheConstants.NamePrefix + testkey, CommandFlags.None))
    .Returns(testvalue);

mockDatabase
    .Setup<string>(_ => _.StringGet(It.IsAny<string>(), It.IsAny<CommandFlags>()))
    .Returns(testvalue);

这将允许您模拟任何

StringGet
调用并查看是否会产生任何效果。

如果是,那就改回一个参数,再试,再改,直到找到问题:)

© www.soinside.com 2019 - 2024. All rights reserved.