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

ASP.NET MVC项目单元测试遇System.NullReferenceException问题求助

问题排查与解决方案

核心问题分析

  • 空引用异常根源:测试类中使用new SaltWaterTankMetricRepository()无参构造函数实例化仓储,而该构造函数并未初始化_dbContext字段。当调用Create方法执行_dbContext.Add(metrics)时,_dbContext为null,直接抛出NullReferenceException。
  • 断言逻辑错误:Create方法返回的是metrics.MetricsID(int类型),但测试中用result.Should().Be(2022-11-8, "78.0", "8.3", ".003", "8.2"),参数类型和数量完全不匹配,即使解决空引用问题,断言也会失败。

修复方案

方案1:使用EF内存数据库进行集成测试

这种方式可以模拟真实的EF操作,无需依赖真实数据库,适合验证仓储逻辑的正确性:

修改测试类代码:

namespace SaltWaterTankMetrics.DataAccess.EF.test
{
    public class SaltWaterTankMetricTest
    {
        private readonly SaltWaterTankMetricRepository _sut;
        private readonly SaltwaterTankContext _dbContext;

        public SaltWaterTankMetricTest()
        {
            // 配置内存数据库
            var options = new DbContextOptionsBuilder<SaltwaterTankContext>()
                .UseInMemoryDatabase(databaseName: "TestTankDb")
                .Options;
            
            _dbContext = new SaltwaterTankContext(options);
            _sut = new SaltWaterTankMetricRepository(_dbContext);
        }

        [Theory]
        [InlineData("2022-11-8", 78.0, 8.3, .003, 8.2)]
        public void CreateMetric_ShouldPass(DateTime date, decimal temp, decimal ph, decimal nh3, decimal dkh)
        {
            // Arrange
            var createMetrics = new SaltWaterTankMetric
            {
                MetricsDate = date,
                SaltWaterTemp = temp,
                SaltWaterPH = ph,
                SaltWaterNH3 = nh3,
                SaltWaterDKH = dkh
            };

            // Act
            var resultId = _sut.Create(createMetrics);
            var createdMetric = _dbContext.SaltWaterTankMetrics.FirstOrDefault(m => m.MetricsID == resultId);

            // Assert
            resultId.Should().BeGreaterThan(0); // 验证ID生成正常
            createdMetric.Should().NotBeNull();
            createdMetric.MetricsDate.Should().Be(date);
            createdMetric.SaltWaterTemp.Should().Be(temp);
            createdMetric.SaltWaterPH.Should().Be(ph);
            createdMetric.SaltWaterNH3.Should().Be(nh3);
            createdMetric.SaltWaterDKH.Should().Be(dkh);
        }
    }
}

方案2:使用Mock框架进行纯单元测试

如果只想测试仓储逻辑,不依赖EF实现,可以用Moq框架Mock DbContext和DbSet:

首先确保安装Moq包,然后修改测试类:

using Moq;
using Microsoft.EntityFrameworkCore;

namespace SaltWaterTankMetrics.DataAccess.EF.test
{
    public class SaltWaterTankMetricTest
    {
        private readonly SaltWaterTankMetricRepository _sut;
        private readonly Mock<SaltwaterTankContext> _mockContext;

        public SaltWaterTankMetricTest()
        {
            _mockContext = new Mock<SaltwaterTankContext>();
            // Mock DbSet
            var mockSet = new Mock<DbSet<SaltWaterTankMetric>>();
            _mockContext.Setup(c => c.SaltWaterTankMetrics).Returns(mockSet.Object);
            // 模拟Add和SaveChanges行为
            _mockContext.Setup(c => c.Add(It.IsAny<SaltWaterTankMetric>()))
                .Callback<SaltWaterTankMetric>(m => m.MetricsID = 1); // 模拟ID赋值
            _mockContext.Setup(c => c.SaveChanges()).Returns(1);

            _sut = new SaltWaterTankMetricRepository(_mockContext.Object);
        }

        [Theory]
        [InlineData("2022-11-8", 78.0, 8.3, .003, 8.2)]
        public void CreateMetric_ShouldPass(DateTime date, decimal temp, decimal ph, decimal nh3, decimal dkh)
        {
            var createMetrics = new SaltWaterTankMetric
            {
                MetricsDate = date,
                SaltWaterTemp = temp,
                SaltWaterPH = ph,
                SaltWaterNH3 = nh3,
                SaltWaterDKH = dkh
            };

            var resultId = _sut.Create(createMetrics);

            resultId.Should().Be(1); // 验证Mock返回的ID
            _mockContext.Verify(c => c.Add(createMetrics), Times.Once);
            _mockContext.Verify(c => c.SaveChanges(), Times.Once);
        }
    }
}

额外说明

  • 原项目运行正常是因为实际环境中会通过依赖注入(DI)给仓储传入已初始化的SaltwaterTankContext实例,而测试中没有这个注入过程,导致上下文为空。
  • 建议移除无参构造函数,强制通过构造函数注入上下文,从根源避免此类空引用问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 02:05:17