这是我对程序中缓存的测试:
[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()));
}
您可以尝试以不同的方式设置模拟以匹配更广泛的参数,并查看问题出在哪里。
所以改变这个
mockDatabase
.Setup<string>(_ => _.StringGet(CacheConstants.NamePrefix + testkey, CommandFlags.None))
.Returns(testvalue);
到
mockDatabase
.Setup<string>(_ => _.StringGet(It.IsAny<string>(), It.IsAny<CommandFlags>()))
.Returns(testvalue);
这将允许您模拟任何
StringGet
调用并查看是否会产生任何效果。
如果是,那就改回一个参数,再试,再改,直到找到问题:)