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

如何为使用AutoMapper和依赖注入的.NET Core 2.0服务编写xUnit测试

作为从Java转.NET的新手,我完全理解你刚接触.NET测试生态的困惑——别担心,xUnit加上Moq(.NET社区最常用的Mocking库)就能轻松搞定这个服务的单元测试。下面我一步步给你拆解实现思路和代码示例:

准备工作:安装必要的NuGet包

首先你需要在测试项目中安装以下依赖:

  • xunit:核心测试框架
  • xunit.runner.visualstudio:让Visual Studio能识别并运行xUnit测试
  • Moq:用于模拟依赖注入的接口对象
  • AutoMapper(如果你的项目已经使用):如果需要测试真实的映射逻辑而非Mock映射

可以通过NuGet包管理器控制台安装:

Install-Package xunit
Install-Package xunit.runner.visualstudio
Install-Package Moq
Install-Package AutoMapper
测试核心思路

你的SubmissionCategoryService依赖IMapper和ISubmissionCategoryRepository两个接口,单元测试的核心是:

  1. 用Moq创建这两个接口的模拟对象,控制它们的返回行为
  2. 将模拟对象注入到SubmissionCategoryService实例中
  3. 调用服务的目标方法,验证返回结果是否符合预期
  4. 验证模拟对象的方法是否被正确调用(确保服务逻辑走了正确的依赖路径)
具体测试案例示例

假设你的SubmissionCategoryService有一个GetAllSubmissionCategoriesAsync()方法,用于返回SubmissionCategoryViewModel集合。下面是完整的测试类:

using Xunit;
using Moq;
using AutoMapper;
using YourAppNamespace.Services;
using YourAppNamespace.Repositories;
using YourAppNamespace.ViewModels;
using YourAppNamespace.Entities;
using System.Collections.Generic;
using System.Threading.Tasks;

public class SubmissionCategoryServiceTests
{
    // 声明模拟对象和待测试的服务实例
    private readonly Mock<ISubmissionCategoryRepository> _mockRepo;
    private readonly Mock<IMapper> _mockMapper;
    private readonly SubmissionCategoryService _service;

    // 构造函数中初始化模拟对象和服务
    public SubmissionCategoryServiceTests()
    {
        _mockRepo = new Mock<ISubmissionCategoryRepository>();
        _mockMapper = new Mock<IMapper>();
        _service = new SubmissionCategoryService(_mockMapper.Object, _mockRepo.Object);
    }

    [Fact]
    public async Task GetAllSubmissionCategoriesAsync_WhenDataExists_ReturnsCorrectViewModelCollection()
    {
        // Arrange:准备测试数据和模拟行为
        var testEntities = new List<SubmissionCategory>
        {
            new SubmissionCategory { Id = 1, Name = "Bug反馈", Description = "提交应用程序Bug" },
            new SubmissionCategory { Id = 2, Name = "功能需求", Description = "提出新功能建议" }
        };

        var expectedViewModels = new List<SubmissionCategoryViewModel>
        {
            new SubmissionCategoryViewModel { Id = 1, Name = "Bug反馈", Description = "提交应用程序Bug" },
            new SubmissionCategoryViewModel { Id = 2, Name = "功能需求", Description = "提出新功能建议" }
        };

        // 模拟Repository返回测试实体集合
        _mockRepo.Setup(repo => repo.GetAllAsync()).ReturnsAsync(testEntities);

        // 模拟Mapper将每个实体映射到对应的ViewModel
        for (int i = 0; i < testEntities.Count; i++)
        {
            _mockMapper.Setup(m => m.Map<SubmissionCategoryViewModel>(testEntities[i]))
                       .Returns(expectedViewModels[i]);
        }

        // Act:调用服务的目标方法
        var result = await _service.GetAllSubmissionCategoriesAsync();

        // Assert:验证结果和依赖调用行为
        // 1. 验证返回集合的数量和内容正确性
        Assert.Equal(expectedViewModels.Count, result.Count);
        for (int i = 0; i < result.Count; i++)
        {
            Assert.Equal(expectedViewModels[i].Id, result[i].Id);
            Assert.Equal(expectedViewModels[i].Name, result[i].Name);
            Assert.Equal(expectedViewModels[i].Description, result[i].Description);
        }

        // 2. 验证Repository的方法被正确调用一次
        _mockRepo.Verify(repo => repo.GetAllAsync(), Times.Once);

        // 3. 验证每个实体的映射方法都被调用一次
        foreach (var entity in testEntities)
        {
            _mockMapper.Verify(m => m.Map<SubmissionCategoryViewModel>(entity), Times.Once);
        }
    }

    [Fact]
    public async Task GetAllSubmissionCategoriesAsync_WhenNoData_ReturnsEmptyCollection()
    {
        // Arrange:模拟Repository返回空集合
        _mockRepo.Setup(repo => repo.GetAllAsync()).ReturnsAsync(new List<SubmissionCategory>());

        // Act:调用服务方法
        var result = await _service.GetAllSubmissionCategoriesAsync();

        // Assert:验证返回空集合,且相关方法被调用
        Assert.Empty(result);
        _mockRepo.Verify(repo => repo.GetAllAsync(), Times.Once);
    }
}
额外优化提示

如果你想同时测试AutoMapper的映射配置是否正确(而非仅Mock映射行为),可以在测试中创建真实的Mapper实例:

// 在测试方法中初始化真实的Mapper
var mapperConfig = new MapperConfiguration(cfg => 
{
    cfg.AddProfile<YourMappingProfile>(); // 替换为你项目中的映射配置类
});
var realMapper = mapperConfig.CreateMapper();

// 用真实Mapper替换Mock的Mapper注入到服务
var serviceWithRealMapper = new SubmissionCategoryService(realMapper, _mockRepo.Object);

这种方式可以同时验证映射逻辑的正确性,适合需要确保实体和ViewModel字段匹配的场景。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.22 08:49:13