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

C#单元测试Mock IStringLocalizerFactory抛出NullReferenceException

问题根因

空引用的核心原因是Mock配置执行时机晚于服务实例化时机:

  • 你在测试类构造函数中直接初始化了EventService实例,构造函数执行时会立刻调用IStringLocalizerFactory.Create()方法给_localizer赋值
  • 但Create()方法的Mock Setup逻辑写在了具体的测试方法内部,服务初始化时这个配置还没生效,Moq对未配置的引用类型返回值默认返回null,最终导致_localizer字段为null
  • 你在测试方法中写的Setup是服务实例化完成后才执行的,无法回溯修改构造函数阶段已经拿到的返回值

另外你当前的写法还有一个潜在问题:就算配置时机正确,你返回的真实StringLocalizer<Resource>实例是传入Mock的IStringLocalizerFactory构造的,内部逻辑依赖factory的其他未配置方法,运行时可能出现其他非预期错误。

解决方案

推荐直接MockIStringLocalizer实例,跳过真实StringLocalizer的内部实现逻辑,同时把Mock配置放在服务实例化之前执行即可。

通用配置写法(适合所有测试用例复用同一套本地化逻辑)

直接在测试类构造函数中先完成所有Mock的通用配置,再初始化服务:

public class EventServiceTests
{
    private readonly Mock<IEventRepository> _eventRepoMock = new Mock<IEventRepository>();
    private readonly Mock<IMemberEventRepository> _memberEventRepoMock = new Mock<IMemberEventRepository>();
    private readonly Mock<IStringLocalizerFactory> _stringLocalizerFactoryMock = new Mock<IStringLocalizerFactory>();
    private readonly EventService _service;

    public EventServiceTests()
    {
        // 1. 先配置Localizer相关Mock
        var localizerMock = new Mock<IStringLocalizer>();
        localizerMock.Setup(l => l["This member already participates in this event."])
            .Returns(new LocalizedString(
                "This member already participates in this event.", 
                "该成员已报名此活动"
            ));
        _stringLocalizerFactoryMock.Setup(f => f.Create(typeof(Resource)))
            .Returns(localizerMock.Object);

        // 2. 所有Mock配置完成后再实例化待测服务
        _service = new EventService(
            _eventRepoMock.Object, 
            _memberEventRepoMock.Object, 
            _stringLocalizerFactoryMock.Object
        );
    }

    [Fact]
    public async Task CreateEventRegistrationAsync_ShouldThrowArgumentException_WhenMemberAlreadyRegisteredForEvent()
    {
        // Arrange
        int eventId = 456;
        int memberId = 123;
        var registration = new MemberEvent
        {
            EventId = eventId,
            MemberId = memberId
        };
        _memberEventRepoMock.Setup(x => x.GetMemberEvent(eventId, memberId))
            .ReturnsAsync(registration);

        // Act & Assert
        var exception = await Assert.ThrowsAsync<ArgumentException>(
            () => _service.CreateEventRegistrationAsync(registration)
        );
        Assert.Equal("该成员已报名此活动", exception.Message);
    }
}

单测试用例独立配置写法(适合不同用例需要不同本地化返回的场景)

如果不同测试方法需要不同的本地化返回值,不要在构造函数中初始化待测服务,把服务初始化逻辑挪到每个测试方法内部,完成针对性Mock配置后再实例化服务即可,避免用例之间的配置互相污染。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 00:45:32