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

MAUI中IPreferences Mock测试报错:表达式树含可选参数调用

问题原因及解决方案

报错原因

你遇到的「表达式树不能包含使用可选参数的调用或调用」错误,本质是Moq无法解析带有可选参数的方法调用表达式。IPreferences.Get<T>方法的第二个参数通常是可选参数(定义为T defaultValue = default(T)),当你在Setup里直接传入DateTime.MinValue时,Moq会将这个调用判定为包含可选参数的操作,导致表达式树生成失败。

解决步骤

1. 修正Get方法的Setup代码

需要明确指定Get方法的泛型类型为DateTime,并用It.IsAny<DateTime>()匹配默认参数(如果需要精确匹配目标键,也可以指定具体的键值):

var preferencesMock = new Mock<IPreferences>();
var lastUpdate = new DateTime(2024, 9, 18, 23, 0, 0);

// 精确匹配业务代码中的_lastUpdateKey对应的键(比如假设是"LastMileageUpdate")
preferencesMock
    .Setup(p => p.Get<DateTime>(It.Is<string>(k => k == "LastMileageUpdate"), It.IsAny<DateTime>()))
    .Returns(lastUpdate);

如果不需要限制键,也可以简化为:

preferencesMock
    .Setup(p => p.Get<DateTime>(It.IsAny<string>(), It.IsAny<DateTime>()))
    .Returns(lastUpdate);

2. 修复Verify部分的错误

你的测试代码中Verify的Set调用写法有误,业务代码是直接存储DateTime类型,不是字符串。应该直接验证Set方法的参数:

// 验证更新时间的Set操作
preferencesMock.Verify(p => p.Set(It.Is<string>(k => k == "LastMileageUpdate"), It.IsAny<DateTime>()), Times.Once);
// 同时验证里程和上次发送里程的重置操作
preferencesMock.Verify(p => p.Set(It.Is<string>(k => k == "MileageKey"), 0.0), Times.Once);
preferencesMock.Verify(p => p.Set(It.Is<string>(k => k == "LastSentMileageKey"), 0.0), Times.Once);

3. 优化建议:固定时间依赖(可选但推荐)

业务代码直接使用DateTime.Now会导致测试不稳定,建议通过依赖注入抽象时间获取逻辑:

  • 定义时间提供接口:
public interface IDateTimeProvider
{
    DateTime Now { get; }
}
  • 修改业务方法依赖该接口:
public class MileageService
{
    private readonly IPreferences _preferences;
    private readonly IDateTimeProvider _dateTimeProvider;
    // 其他依赖...

    public MileageService(IPreferences preferences, IDateTimeProvider dateTimeProvider, /* 其他参数 */)
    {
        _preferences = preferences;
        _dateTimeProvider = dateTimeProvider;
        // 初始化其他依赖
    }

    public void ResetMileageIfNewDay()
    {
        var lastUpdate = preferences.Get(_lastUpdateKey, DateTime.MinValue);
        var resetTime = _dateTimeProvider.Now.Date.AddHours(3);
        
        if (lastUpdate.Date < resetTime)
        {
            preferences.Set(_mileageKey, 0.0);
            preferences.Set(_lastUpdateKey, _dateTimeProvider.Now.Date);
            preferences.Set(_lastSentMileageKey, 0.0);
            _logger.LogInformation("Mileage reset due to new day at 3AM");
        }
    }
}
  • 测试时Mock时间提供器,固定触发重置的时间:
var dateTimeMock = new Mock<IDateTimeProvider>();
// 设置为次日4点,确保满足重置条件
dateTimeMock.Setup(d => d.Now).Returns(new DateTime(2024, 9, 19, 4, 0, 0));

var service = new MileageService(preferencesMock.Object, dateTimeMock.Object, default, default, default);

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 04:02:24