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

单元测试时遭遇System.NullReferenceException问题求助

排查Minimal API单元测试空引用异常问题

问题根源

你的ParticipantsGetAll类中,私有字段participantService仅在AddRoute方法定义的路由委托被实际API请求触发时,才会从DI容器获取实例赋值。但单元测试中你直接实例化ParticipantsGetAll后调用HandleAsync,这个字段从未被赋值,始终为null,执行participantService!.ListAllAsync(query)时自然触发NullReferenceException。


解决方案

方案1:重构为构造函数注入(推荐)

将依赖通过构造函数注入,让类的依赖关系更清晰,同时方便测试时传入Mock实例。

修改ParticipantsGetAll代码:

public class ParticipantsGetAll : IEndpoint<IResult, GetAllParticipantsRequest>
{
    const string uri = "api/participants-all";
    private readonly ParticipantService _participantService;

    // 构造函数注入依赖
    public ParticipantsGetAll(ParticipantService participantService)
    {
        _participantService = participantService;
    }

    public void AddRoute(IEndpointRouteBuilder app)
    {
        app.MapPost(uri, async ([FromBody] GetAllParticipantsRequest query) => 
        {
            return await HandleAsync(query);
        })
        .Produces<List<ParticipantSummaryModel>>()
        .WithTags("Participants")
        .WithName("GetAllParticipants");
    }

    public async Task<IResult> HandleAsync(GetAllParticipantsRequest query)
    {
        var participants = await _participantService.ListAllAsync(query);
        return Results.Ok(participants);
    }
}

修改测试代码(用Moq创建Mock服务):

[Fact]
public async Task ListAllAsyncShouldReturn200Status() // 替换async void为async Task,xunit推荐写法
{
    // 准备测试数据
    var query = new GetAllParticipantsRequest()
    {
        Name= "",
        KeyCordinator="",
        RelatedConnection="",
        Statuses = null
    };
    var mockParticipants = new List<ParticipantSummaryModel> { ParticipantMockData.NewParticipantModel() };

    // Mock ParticipantService
    var mockParticipantService = new Mock<ParticipantService>();
    mockParticipantService.Setup(s => s.ListAllAsync(query))
                          .ReturnsAsync(mockParticipants);

    // 实例化被测类,传入Mock服务
    var sut = new ParticipantsGetAll(mockParticipantService.Object);

    // 执行测试
    var result = await sut.HandleAsync(query);

    // 断言
    var okResult = Assert.IsType<OkObjectResult>(result);
    okResult.StatusCode.Should().Be(200);
    var returnedParticipants = Assert.IsType<List<ParticipantSummaryModel>>(okResult.Value);
    returnedParticipants.Should().BeEquivalentTo(mockParticipants);
}

方案2:测试完整API端点(贴近真实请求场景)

如果想验证整个路由流程,可使用WebApplicationFactory搭建测试服务器,模拟真实请求:

public class ParticipantsApiTests : IClassFixture<WebApplicationFactory<Program>>
{
    private readonly HttpClient _client;

    public ParticipantsApiTests(WebApplicationFactory<Program> factory)
    {
        // 替换DI容器中的真实服务为Mock
        var mockFactory = factory.WithWebHostBuilder(builder =>
        {
            builder.ConfigureServices(services =>
            {
                var descriptor = services.SingleOrDefault(d => d.ServiceType == typeof(ParticipantService));
                if (descriptor != null) services.Remove(descriptor);

                var mockService = new Mock<ParticipantService>();
                mockService.Setup(s => s.ListAllAsync(It.IsAny<GetAllParticipantsRequest>()))
                          .ReturnsAsync(new List<ParticipantSummaryModel> { ParticipantMockData.NewParticipantModel() });

                services.AddSingleton(mockService.Object);
            });
        });

        _client = mockFactory.CreateClient();
    }

    [Fact]
    public async Task GetAllParticipants_ReturnsOkStatus()
    {
        // 准备请求内容
        var query = new GetAllParticipantsRequest()
        {
            Name= "",
            KeyCordinator="",
            RelatedConnection="",
            Statuses = null
        };
        var content = new StringContent(JsonSerializer.Serialize(query), Encoding.UTF8, "application/json");

        // 发送POST请求
        var response = await _client.PostAsync("/api/participants-all", content);

        // 断言
        response.EnsureSuccessStatusCode();
        Assert.Equal(HttpStatusCode.OK, response.StatusCode);

        var returnedParticipants = await JsonSerializer.DeserializeAsync<List<ParticipantSummaryModel>>(
            await response.Content.ReadAsStreamAsync(),
            new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
        
        returnedParticipants.Should().NotBeEmpty();
    }
}

内容的提问来源于stack exchange,提问作者hanushi-thana

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 18:35:16