Xamarin.Forms单元测试:如何用Autofac.Extras.Moq模拟Bootstrap静态容器服务?
解决ViewModel依赖静态容器的单元测试模拟问题
问题根源
你的ViewModel直接依赖Bootstrap.container这个静态服务容器,属于紧耦合设计,导致测试时无法轻松替换依赖服务。下面提供两种可行的解决思路:
方案一:重构ViewModel使用构造函数注入(推荐)
这是最符合依赖注入原则的方案,从根源上解耦ViewModel与静态容器:
1. 修改ViewModel代码
将原来从静态容器获取服务的逻辑,改为构造函数注入:
public class MyViewModel { private readonly IMyService _myService; // 通过构造函数传入依赖,而非直接调用静态容器 public MyViewModel(IMyService myService) { _myService = myService ?? throw new ArgumentNullException(nameof(myService)); } // 业务方法示例 public string ExecuteServiceMethod() { return _myService.GetData(); } }
2. 编写单元测试
直接用Autofac.Extras.Moq创建模拟服务并注入ViewModel,完全不需要依赖Bootstrap:
[TestFixture] public class MyViewModelTests { private AutoMock _autoMock; private MyViewModel _viewModel; [SetUp] public void Setup() { _autoMock = AutoMock.GetLoose(); // 从AutoMock容器中解析ViewModel,自动注入模拟的IMyService _viewModel = _autoMock.Create<MyViewModel>(); } [Test] public void ExecuteServiceMethod_ReturnsMockedData() { // 配置模拟服务的行为 var mockService = _autoMock.Mock<IMyService>(); mockService.Setup(s => s.GetData()).Returns("Mocked Test Data"); // 执行测试 var result = _viewModel.ExecuteServiceMethod(); // 断言结果 Assert.AreEqual("Mocked Test Data", result); mockService.Verify(s => s.GetData(), Times.Once); } [TearDown] public void Teardown() { _autoMock.Dispose(); } }
方案二:不修改ViewModel,替换Bootstrap的静态容器
如果无法重构现有代码,可以通过修改Bootstrap类,允许测试时替换服务容器:
1. 调整Bootstrap类
暴露可修改的容器入口,方便测试替换:
public static class Bootstrap { // 将容器设为internal,允许测试项目访问(需在主项目添加InternalsVisibleTo特性) internal static IServiceProvider Container { get; private set; } public static void Start() { var services = new ServiceCollection(); services.AddSingleton<IMyService, MyService>(); Container = services.BuildServiceProvider(); } // 新增测试专用方法,用于替换容器 internal static void SetTestContainer(IServiceProvider testContainer) { Container = testContainer; } }
2. 编写单元测试
在测试初始化时,构建包含模拟服务的容器并替换Bootstrap的容器:
[TestFixture] public class MyViewModelTests { private Mock<IMyService> _mockService; [SetUp] public void Setup() { _mockService = new Mock<IMyService>(); // 构建测试用的服务容器 var testServices = new ServiceCollection(); testServices.AddSingleton<IMyService>(_mockService.Object); var testContainer = testServices.BuildServiceProvider(); // 替换Bootstrap的容器 Bootstrap.SetTestContainer(testContainer); } [Test] public void ExecuteServiceMethod_UsesMockedService() { // 配置模拟行为 _mockService.Setup(s => s.GetData()).Returns("Test Data From Mock"); // 实例化ViewModel,此时会从替换后的容器获取服务 var viewModel = new MyViewModel(); var result = viewModel.ExecuteServiceMethod(); // 断言 Assert.AreEqual("Test Data From Mock", result); } [TearDown] public void Teardown() { // 测试后重置Bootstrap容器,避免影响其他测试 Bootstrap.Start(); } }
关键注意事项
- 测试之间一定要隔离:每次测试结束后重置
Bootstrap容器,防止测试用例互相干扰。 - 优先选择构造注入方案:静态容器依赖会导致代码难以维护和扩展,重构后不仅测试更简单,代码质量也会提升。
内容的提问来源于stack exchange,提问作者Sushmitha Rao B
相关产品推荐
相关产品推荐

