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

ASP.NET Core 3 MVC控制器新手单元测试编写求助

.NET Core Controller 单元测试实现方案

前置依赖准备

测试项目需要先安装以下Nuget包,版本和你项目主版本保持一致即可:

<PackageReference Include="xunit" Version="2.4.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5" />
<PackageReference Include="Moq" Version="4.18.4" />
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="6.0.25" />

Create 方法单元测试示例

测试逻辑覆盖:提交合法公司信息时,数据正确入库且跳转至列表页

using Xunit;
using Moq;
using Microsoft.EntityFrameworkCore;
using Microsoft.AspNetCore.Mvc;
// 替换为你项目对应命名空间
using YourProject.Controllers;
using YourProject.Data;
using YourProject.Models;
using YourProject.Services;

public class CompanyControllerTests
{
    // 构造独立的内存数据库上下文,避免多测试用例数据干扰
    private CodingChallegeDBContext GetTestDbContext()
    {
        var options = new DbContextOptionsBuilder<CodingChallegeDBContext>()
            .UseInMemoryDatabase(Guid.NewGuid().ToString())
            .Options;
        var context = new CodingChallegeDBContext(options);
        context.Database.EnsureCreated();
        return context;
    }

    [Fact]
    public async Task Create_ValidModel_AddsCompanyAndRedirects()
    {
        // 初始化依赖
        var mockPageService = new Mock<IPaginatedListService>();
        var testDbContext = GetTestDbContext();
        var controller = new CompanyController(testDbContext, mockPageService.Object);

        // 构造测试数据
        var testCompany = new Company
        {
            Id = 1,
            Name = "测试科技有限公司",
            Address = "北京市海淀区测试路1号"
            // 补全你Company模型所有必填字段
        };

        // 执行测试方法
        var result = await controller.Create(testCompany);

        // 断言逻辑
        // 验证返回结果为跳转到Index页
        var redirectRes = Assert.IsType<RedirectToActionResult>(result);
        Assert.Equal(nameof(CompanyController.Index), redirectRes.ActionName);
        // 验证数据已正确写入数据库
        var savedCompany = await testDbContext.Companies.FindAsync(1);
        Assert.NotNull(savedCompany);
        Assert.Equal(testCompany.Name, savedCompany.Name);
    }
}

如果需要测试模型校验失败的场景,可在调用Create方法前手动给ModelState注入错误:

controller.ModelState.AddModelError("Name", "公司名称为必填项");

Index 方法单元测试示例

测试逻辑覆盖:分页查询公司列表时,正确调用分页服务并返回对应数据

[Fact]
public async Task Index_Get_ReturnsPaginatedCompanyList()
{
    // 初始化依赖
    var mockPageService = new Mock<IPaginatedListService>();
    var testDbContext = GetTestDbContext();

    // 预置测试数据到内存库
    var testCompanies = new List<Company>
    {
        new Company{ Id = 1, Name = "公司A"},
        new Company{ Id = 2, Name = "公司B"},
        new Company{ Id = 3, Name = "公司C"}
    };
    await testDbContext.Companies.AddRangeAsync(testCompanies);
    await testDbContext.SaveChangesAsync();

    // 配置Mock分页服务的返回值
    var expectedPageData = new PaginatedList<Company>(testCompanies, 3, 1, 2);
    mockPageService
        .Setup(s => s.CreateAsync(It.IsAny<IQueryable<Company>>(), It.IsAny<int>(), It.IsAny<int>()))
        .ReturnsAsync(expectedPageData);

    var controller = new CompanyController(testDbContext, mockPageService.Object);

    // 执行测试方法,查询第1页,每页2条
    var result = await controller.Index(pageNumber: 1, pageSize: 2);

    // 断言逻辑
    var viewRes = Assert.IsType<ViewResult>(result);
    var resultData = Assert.IsType<PaginatedList<Company>>(viewRes.Model);
    Assert.Equal(3, resultData.TotalCount);
    Assert.Equal(1, resultData.PageIndex);
    // 验证分页服务被正确调用一次
    mockPageService.Verify(s => s.CreateAsync(It.IsAny<IQueryable<Company>>(), 1, 2), Times.Once);
}

入门学习建议

  1. 先从无业务依赖的简单方法入手,熟悉xUnit的基础断言语法
  2. 掌握Moq的核心用法:接口模拟、返回值配置、调用次数验证
  3. 明确EF Core内存数据库的适用场景,区分复杂查询场景下Mock上下文和使用内存库的差异

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.24 10:06:01