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

如何用Moq、xUnit和In-Memory Db测试CQRS仓储层方法

仓储层单元测试实现指南(Moq + xUnit + EF Core In-Memory)

先修正代码中的已知错误

在开始编写测试前,先修复你提供的代码里的几个拼写/逻辑错误,否则测试会失败:

  • IGenericRepository<TContext, TEntity> 应该是 GenericRepository<TContext, TEntity>(类实现接口,而非接口继承)
  • GamesRepository 中的 readomly 改为 readonly
  • GamesRepository 构造函数里的 _gamesDbContext - dbContext 改为 _gamesDbContext = dbContext
  • GetByGameNameAsync 方法里的 v.Entity == gameName 改为 v.gameName == gameName(实体类属性为gameName)

步骤1:安装必要NuGet包

在测试项目中安装以下包:

Install-Package Microsoft.EntityFrameworkCore.InMemory
Install-Package xunit
Install-Package xunit.runner.visualstudio
Install-Package Moq
Install-Package Microsoft.NET.Test.Sdk

步骤2:编写测试类

创建 GamesRepositoryTests 类,实现两个方法的单元测试:

using Xunit;
using Microsoft.EntityFrameworkCore;
using YourNamespace; // 替换为你的项目命名空间

public class GamesRepositoryTests
{
    // 辅助方法:生成带测试数据的In-Memory DbContext
    private GamesDbContext CreateTestDbContext(List<GamesEntity> testData)
    {
        var options = new DbContextOptionsBuilder<GamesDbContext>()
            .UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString()) // 用唯一库名避免测试间数据干扰
            .Options;

        var context = new GamesDbContext(options);
        context.GamesEntity.AddRange(testData);
        context.SaveChanges();
        return context;
    }

    #region GetAllAsync 测试
    [Fact]
    public async Task GetAllAsync_ReturnsAllEntities()
    {
        // 1. 准备测试数据
        var testGames = new List<GamesEntity>
        {
            new GamesEntity { Id = 1, gameName = "原神", gameDescription = "开放世界RPG" },
            new GamesEntity { Id = 2, gameName = "星穹铁道", gameDescription = "回合制RPG" }
        };

        // 2. 创建In-Memory DbContext
        using var context = CreateTestDbContext(testGames);

        // 3. 实例化仓储
        var repository = new GamesRepository(context);

        // 4. 调用待测试方法
        var result = await repository.GetAllAsync();

        // 5. 验证结果
        Assert.NotNull(result);
        Assert.Equal(testGames.Count, result.Count);
        Assert.Contains(result, g => g.gameName == "原神");
        Assert.Contains(result, g => g.gameName == "星穹铁道");
    }
    #endregion

    #region GetByGameNameAsync 测试
    [Fact]
    public async Task GetByGameNameAsync_ExistingName_ReturnsMatchingEntities()
    {
        // 1. 准备测试数据
        var testGames = new List<GamesEntity>
        {
            new GamesEntity { Id = 1, gameName = "原神", gameDescription = "开放世界RPG" },
            new GamesEntity { Id = 2, gameName = "星穹铁道", gameDescription = "回合制RPG" },
            new GamesEntity { Id = 3, gameName = "原神", gameDescription = "国际服" }
        };

        // 2. 创建In-Memory DbContext
        using var context = CreateTestDbContext(testGames);

        // 3. 实例化仓储
        var repository = new GamesRepository(context);

        // 4. 调用待测试方法
        var result = await repository.GetByGameNameAsync("原神");

        // 5. 验证结果
        Assert.NotNull(result);
        Assert.Equal(2, result.Count);
        Assert.All(result, g => Assert.Equal("原神", g.gameName));
    }

    [Fact]
    public async Task GetByGameNameAsync_NonExistingName_ReturnsEmptyList()
    {
        // 1. 准备测试数据
        var testGames = new List<GamesEntity>
        {
            new GamesEntity { Id = 1, gameName = "原神", gameDescription = "开放世界RPG" }
        };

        // 2. 创建In-Memory DbContext
        using var context = CreateTestDbContext(testGames);

        // 3. 实例化仓储
        var repository = new GamesRepository(context);

        // 4. 调用待测试方法
        var result = await repository.GetByGameNameAsync("塞尔达传说");

        // 5. 验证结果
        Assert.NotNull(result);
        Assert.Empty(result);
    }
    #endregion
}

关键说明

  1. In-Memory数据库优势:无需真实数据库连接,测试速度快,且每次测试用唯一库名避免数据污染。
  2. 测试流程:严格遵循「准备数据→创建依赖→调用方法→验证结果」的单元测试标准流程。
  3. 异步处理:所有测试方法标记async,调用仓储方法时用await,确保异步逻辑正确执行。
  4. 边界覆盖:GetByGameNameAsync测试了「存在匹配数据」和「无匹配数据」两种场景,保证方法逻辑完整性。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 07:30:53