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

如何为基于SQL Server与EF Core的ASP.NET Core应用编写测试及控制器单元测试

ASP.NET Core控制器单元测试(EF Core+SQL Server场景)

测试优先级

先测服务层,再测控制器。服务是业务逻辑的核心,控制器仅负责请求转发、参数校验、视图/结果返回,本质是依赖服务的中间层。先覆盖服务核心逻辑的测试,再验证控制器对服务的调用逻辑,能让测试更高效,问题定位更清晰。

控制器单元测试:Mock依赖服务

控制器单元测试的核心是隔离外部依赖,绝对不能直接连接生产SQL Server。用Moq这类框架模拟服务的行为,验证控制器的输出是否符合预期即可。

示例代码

先看基础的控制器、服务定义:

// 目标控制器
public class HomeController : Controller
{
    private readonly IDataService _service;

    public HomeController(IDataService service)
    {
        _service = service;
    }

    public IActionResult Index()
    {
        var data = _service.GetAll();
        return View(data);
    }
}

// 服务接口
public interface IDataService
{
    IEnumerable<MyModel> GetAll();
}

// 实体模型
public class MyModel
{
    public int Id { get; set; }
    public string Name { get; set; }
}

以下是控制器的单元测试(基于xunit+Moq):

public class HomeControllerTests
{
    private readonly Mock<IDataService> _mockService;
    private readonly HomeController _controller;

    public HomeControllerTests()
    {
        // 初始化Mock服务实例
        _mockService = new Mock<IDataService>();
        // 注入Mock服务到控制器
        _controller = new HomeController(_mockService.Object);
    }

    [Fact]
    public void Index_ReturnsViewWithCorrectData()
    {
        // 1. 准备测试数据
        var testData = new List<MyModel>
        {
            new() { Id = 1, Name = "测试数据1" },
            new() { Id = 2, Name = "测试数据2" }
        };

        // 2. 配置Mock行为:调用GetAll时返回预设数据
        _mockService.Setup(s => s.GetAll()).Returns(testData);

        // 3. 执行控制器方法
        var result = _controller.Index();

        // 4. 验证结果
        // 确认返回类型是ViewResult
        var viewResult = Assert.IsType<ViewResult>(result);
        // 确认视图模型的类型和内容正确
        var model = Assert.IsAssignableFrom<IEnumerable<MyModel>>(viewResult.Model);
        Assert.Equal(2, model.Count());
        // 验证服务的GetAll方法确实被调用了一次
        _mockService.Verify(s => s.GetAll(), Times.Once);
    }
}

服务层测试:用EF Core内存数据库

服务依赖EF Core的DbContext,测试时推荐用内存数据库模拟真实数据库行为——既隔离了生产环境,又能验证EF Core查询逻辑的正确性。

示例代码

public class DataServiceTests
{
    [Fact]
    public void GetAll_ReturnsAllDatabaseRecords()
    {
        // 1. 配置独立内存数据库(每个测试用唯一名称,避免数据污染)
        var dbOptions = new DbContextOptionsBuilder<AppDbContext>()
            .UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString())
            .Options;

        // 2. 初始化测试数据
        using (var context = new AppDbContext(dbOptions))
        {
            context.MyModels.AddRange(
                new MyModel { Id = 1, Name = "测试数据1" },
                new MyModel { Id = 2, Name = "测试数据2" }
            );
            context.SaveChanges();
        }

        // 3. 实例化服务并执行测试
        using (var context = new AppDbContext(dbOptions))
        {
            var service = new DataService(context);
            var result = service.GetAll();
            
            // 验证返回数据数量正确
            Assert.Equal(2, result.Count());
        }
    }
}

其他测试类型:集成测试

如果要验证从HTTP请求到视图渲染的完整链路正确性,可以用集成测试。通过Microsoft.AspNetCore.Mvc.Testing包启动测试服务器,模拟真实请求流程。

示例代码

public class HomeControllerIntegrationTests : IClassFixture<WebApplicationFactory<Program>>
{
    private readonly HttpClient _httpClient;

    public HomeControllerIntegrationTests(WebApplicationFactory<Program> factory)
    {
        // 创建测试用HttpClient
        _httpClient = factory.CreateClient();
    }

    [Fact]
    public async Task Index_Page_ReturnsSuccessAndContainsData()
    {
        // 发送GET请求到首页
        var response = await _httpClient.GetAsync("/");
        
        // 验证请求成功
        response.EnsureSuccessStatusCode();
        
        // 验证页面内容包含预期数据(假设视图渲染了模型的Name字段)
        var pageContent = await response.Content.ReadAsStringAsync();
        Assert.Contains("测试数据1", pageContent);
    }
}

常见错误修正

如果你的测试直接使用真实DbContext连接SQL Server,这不属于单元测试——这类测试依赖数据库状态,不稳定且执行缓慢。正确的做法是:

  • 控制器测试:必须Mock服务,完全隔离数据库
  • 服务测试:用内存数据库或Mock DbSet,避免依赖生产环境

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 05:20:50