如何在Xamarin.Forms中单元测试Preferences存储值
异常根因
抛出Xamarin.Essentials.NotImplementedInReferenceAssemblyException是因为单元测试项目默认引用的Xamarin.Essentials是仅包含API签名的引用程序集,没有对应Android、iOS、Windows等平台的原生存储实现,脱离平台宿主环境直接调用Preferences静态类的方法必然触发该异常。
另外你代码里写的Assert.Equals是常见误用:这个方法是根类Object的静态方法,不是单元测试框架提供的断言方法,即使存储逻辑正常,也无法达到预期的断言效果,应该用测试框架(xUnit/NUnit/MSTest)提供的Assert.Equal做值比较。
可行测试方案
方案1:使用官方内置测试实现(无需改动业务代码)
Xamarin.Essentials从1.6版本开始内置了专门给单元测试用的内存存储实现,不需要依赖平台环境,只需要在测试初始化阶段注入即可:
- 确保单元测试项目安装的
Xamarin.Essentials包版本和业务项目一致 - 在测试类初始化逻辑中注入内存实现,之后就可以按正常逻辑调用Preferences API:
using Xamarin.Essentials; using Xunit; public class PreferenceTests { public PreferenceTests() { // 注入测试专用内存版实现,数据仅在测试运行时存在于内存 Preferences.SetImplementation(Preferences.InMemoryImplementation); } [Fact] public void AppVersion_AfterSet_ReturnsCorrectValue() { // 执行存储逻辑 Preferences.Set("AppVersion", "1.0"); // 校验结果 var storedVersion = Preferences.Get("AppVersion", string.Empty); Assert.Equal("1.0", storedVersion); } }
该实现不会产生持久化脏数据,每个测试类初始化时会重置存储状态,适合不想改动现有业务代码的场景。
方案2:抽象依赖封装(推荐,符合单元测试隔离原则)
静态类天生是强耦合的,不利于单元测试隔离,架构规范的项目建议对Preferences做接口抽象,业务代码依赖抽象而非具体静态实现:
- 定义存储抽象接口,以及对应Xamarin.Essentials的生产环境实现:
public interface IAppPreferences { void Set<T>(string key, T value); T Get<T>(string key, T defaultValue); } public class EssentialsAppPreferences : IAppPreferences { public T Get<T>(string key, T defaultValue) => Preferences.Get(key, defaultValue); public void Set<T>(string key, T value) => Preferences.Set(key, value); }
- 业务代码通过依赖注入获取
IAppPreferences实例操作,不要直接调用静态Preferences类:
public class AppConfigService { private readonly IAppPreferences _preferences; public AppConfigService(IAppPreferences preferences) { _preferences = preferences; } public void SaveCurrentAppVersion() { _preferences.Set("AppVersion", "1.0"); } }
- 单元测试时不需要依赖Xamarin.Essentials的任何实现,自己写简单的内存版存储即可完成校验:
// 测试专用内存实现 public class InMemoryTestPreferences : IAppPreferences { private readonly Dictionary<string, object> _tempStorage = new(); public T Get<T>(string key, T defaultValue) => _tempStorage.TryGetValue(key, out var val) ? (T)val : defaultValue; public void Set<T>(string key, T value) => _tempStorage[key] = value; } // 测试用例 [Fact] public void SaveCurrentAppVersion_WhenCalled_StoreVersionCorrectly() { var testPrefs = new InMemoryTestPreferences(); var configService = new AppConfigService(testPrefs); configService.SaveCurrentAppVersion(); Assert.Equal("1.0", testPrefs.Get("AppVersion", string.Empty)); }
该方案完全不依赖Xamarin.Essentials的内部实现,测试运行速度更快,也不会因为第三方库版本变动导致测试失败,是长期维护项目的首选方案。如果你用的Xamarin.Essentials版本低于1.6没有内置测试实现,只能用该方案。
内容的提问来源于stack exchange,提问作者MainakChoudhury
相关产品推荐
相关产品推荐

