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
相关产品推荐
相关产品推荐

