You need to enable JavaScript to run this app. 如何使用Moq和xUnit测试泛型仓储模式下的业务逻辑方法
优惠活动
大模型
产品
解决方案
定价
更多

如何使用Moq和xUnit测试泛型仓储模式下的业务逻辑方法

问题根因

你测试拿到null值是两个逻辑完全错位的操作导致的:

  • 你在测试中新建Mock<IFolderService>实例、并为其方法配置返回值的操作完全无效:这个mock对象和你要测试的真实_sut(也就是FolderManager实例)没有任何引用关联,你给mock配置的返回值只会作用在这个独立的mock对象上,完全不会影响真实SUT的执行逻辑。
  • 真实SUT的GetFoldersByUserId方法内部,实际依赖的是IGenericDal<Folder>的GetAllByFilter方法来获取数据;你从未为这个依赖方法配置返回值,Moq框架对未配置的方法默认返回null,这就是最终拿到null结果的直接原因。
正确的测试实现步骤
  1. 完全删除冗余的Mock<IFolderService>相关代码——单元测试的核心是验证真实SUT(被测类)的逻辑,不需要、也不应该Mock被测类自身的接口。
  2. 为SUT真实调用的下游依赖(也就是_folderRepoMock对应的IGenericDal<Folder>.GetAllByFilter方法)配置预设返回值,让它返回你准备的测试样本数据。
  3. 补充空值校验,避免出现null引用的运行时错误。
修正后的测试代码示例
public class FolderServiceTest
{  
    private readonly FolderManager _sut;
    private readonly Mock<IGenericDal<Folder>> _folderRepoMock = new Mock<IGenericDal<Folder>>();
    private readonly Mock<IFolderDal> _folderDalMock = new Mock<IFolderDal>();

    public FolderServiceTest()
    {
        _sut = new FolderManager(_folderDalMock.Object, _folderRepoMock.Object);
    }

    [Fact]
    public async Task GetFoldersByUserId_ShouldReturnListOfFolders_WhenUserExist()
    {
        //Arrange
        // 直接为SUT真实调用的依赖方法打桩,匹配任意传入的过滤表达式
        var sampleFolders = GetSampleFolder();
        _folderRepoMock
            .Setup(dal => dal.GetAllByFilter(It.IsAny<Expression<Func<Folder, bool>>>()))
            .ReturnsAsync(sampleFolders);

        var expected = sampleFolders;

        //Act
        var actual = await _sut.GetFoldersByUserId(1);

        //Assert 
        Assert.NotNull(actual);
        Assert.Equal(expected.Count, actual.Count);

        for (int i = 0; i < expected.Count; i++)
        {
            Assert.Equal(expected[i].FolderName, actual[i].FolderName);
            Assert.Equal(expected[i].Size, actual[i].Size);
        }

        // 可选的严谨性校验:确认依赖方法被正确调用了一次
        _folderRepoMock.Verify(
            dal => dal.GetAllByFilter(It.IsAny<Expression<Func<Folder, bool>>>()), 
            Times.Once);
    }

    // 预先定义的测试数据生成方法
    private List<Folder> GetSampleFolder() => new List<Folder>
    {
        new Folder { FolderName = "工作目录", Size = 1024, AppUserId = 1, IsDeleted = false, ParentFolderId = null },
        new Folder { FolderName = "个人资料", Size = 2048, AppUserId = 1, IsDeleted = false, ParentFolderId = null }
    };
}
额外注意点
  • 打桩带表达式参数的方法时,需要提前引入using System.Linq.Expressions;命名空间,否则会出现编译错误。如果不需要严格校验过滤表达式的具体内容,可以直接用It.IsAny<Expression<Func<Folder,bool>>>()匹配任意传入的条件;如果需要验证过滤逻辑的正确性,可以提取表达式的参数做校验,比如确认表达式中确实包含IsDeleted == false、ParentFolderId == null、AppUserId == 传入的用户id的逻辑。
  • 测试过程中不需要给IFolderDal做任何配置,因为当前测试的GetFoldersByUserId方法完全没有用到这个依赖,Moq默认返回的null值不会影响测试结果。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.29 08:15:04