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

Blazor组件BUnit测试:Moq模拟异步方法失效引发空引用异常

问题:Moq模拟Blazor服务方法未生效,引发NullReferenceException

在为Blazor组件编写BUnit测试时,尝试用Moq模拟一个公开异步方法,但真实方法的代码仍被执行,导致System.NullReferenceException——原因是Service.MockedMethod()中调用的_authenticationStateProvider.GetAuthenticationStateAsync()返回的authState为null。

核心原因分析

  1. 注入的是真实服务而非Mock实例
    测试代码的Setup方法中,注册的是真实的AuthenticationService实例,而非你创建的Mock<IAuthenticationStateService>对象:

    Services.AddSingleton<IAuthenticationStateService>(new AuthenticationService(_mockServiceProvider.Object));
    

    这意味着组件最终使用的是真实服务,自然会执行其内部逻辑。

  2. 组件依赖具体类而非接口
    组件中直接注入的是具体类AuthenticationStateService,而非IAuthenticationStateService接口:

    @inject AuthenticationStateService service
    

    即便你注册了接口的Mock实例,组件也不会使用它,因为它依赖的是具体实现类。

  3. Mock的Setup未关联到实际使用的服务
    你对_mockAuthenticationStateService做的方法Setup完全无效,因为这个Mock对象根本没被注入到服务容器中,组件从未接触过它。

修正方案

推荐方案:依赖接口+注册Mock实例

这种方式符合依赖倒置原则,是最灵活的测试方案。

  1. 修改组件的依赖为接口
    将组件中的注入改为接口类型,同时替换.Result为await避免死锁风险:

    @inject IAuthenticationStateService service
    
    @code {
        protected override async Task OnInitializedAsync()
        {
            UserName = await service.MockedMethod();
        }
    }
    
  2. 调整测试代码,注册Mock接口实例
    修改Setup方法,直接将Mock的接口实例注入服务容器:

    internal class MyComponentTests : BunitTestContext
    {
        private Mock<IAuthenticationStateService> _mockAuthenticationStateService;
    
        [SetUp]
        public void Setup()
        {  
            _mockAuthenticationStateService = new Mock<IAuthenticationStateService>();
            // 注册Mock实例到服务容器
            Services.AddSingleton<IAuthenticationStateService>(_mockAuthenticationStateService.Object);
        }
    
        [Test]
        public void MyComponentRendersCorrectly()
        {
            _mockAuthenticationStateService.Setup(s => s.MockedMethod()).ReturnsAsync("user");
    
            var cut = RenderComponent<MyComponent>();
    
            cut.MarkupMatches("<h1>TEST</h1>");
        }
    }
    

    此时组件会使用Mock对象,MockedMethod的真实代码不会被执行,直接返回预设的"user"。

备选方案:Mock具体类(不推荐)

若因特殊原因无法修改组件依赖为接口,可通过Mock具体类实现,但需要将目标方法改为virtual:

  1. 修改服务类的方法为virtual

    namespace Services;
    
    public class AuthenticationStateService(AuthenticationStateProvider authenticationStateProvider) : IService
    {
        private readonly AuthenticationStateProvider _authenticationStateProvider = authenticationStateProvider;
    
        // 添加virtual关键字,允许Moq拦截
        public virtual async Task<string> MockedMethod()
        {
            // 原有代码
        }
    }
    
  2. 调整测试代码Mock具体类

    internal class MyComponentTests : BunitTestContext
    {
        private Mock<AuthenticationStateService> _mockAuthService;
    
        [SetUp]
        public void Setup()
        {  
            var mockAuthProvider = new Mock<AuthenticationStateProvider>();
            // Mock具体服务类
            _mockAuthService = new Mock<AuthenticationStateService>(mockAuthProvider.Object);
            Services.AddSingleton<AuthenticationStateService>(_mockAuthService.Object);
        }
    
        [Test]
        public void MyComponentRendersCorrectly()
        {
            _mockAuthService.Setup(s => s.MockedMethod()).ReturnsAsync("user");
    
            var cut = RenderComponent<MyComponent>();
    
            cut.MarkupMatches("<h1>TEST</h1>");
        }
    }
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 21:14:55