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

如何用xUnit、Moq结合内存数据库测试ASP.NET Core 6应用?

解决ASP.NET Core 6测试中内存数据库种子数据返回Null的问题

核心问题分析

你用Mock<IArtistaService>时,默认Mock对象不会自动关联Fixture里的内存上下文和真实仓储/服务逻辑,Mock方法默认返回null,这就是拿不到种子数据的根本原因。

解决方案步骤

1. 调整测试策略:用真实服务+内存上下文替代Mock服务

如果目标是验证控制器到数据库的完整业务逻辑,没必要Mock服务层,直接注入真实的服务、仓储和Fixture中的内存上下文即可。

正确的Fixture写法

public class DatabaseFixture : IDisposable
{
    public OrientoonContext Context { get; private set; }

    public DatabaseFixture()
    {
        // 生成唯一内存库名,避免测试间数据污染
        var options = new DbContextOptionsBuilder<OrientoonContext>()
            .UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString())
            .Options;

        Context = new OrientoonContext(options);
        SeedTestData(Context);
    }

    private void SeedTestData(OrientoonContext context)
    {
        var testArtistas = new List<Artista>
        {
            new Artista { Id = 1, Nome = "测试艺术家1" },
            new Artista { Id = 2, Nome = "测试艺术家2" }
        };
        context.Artistas.AddRange(testArtistas);
        context.SaveChanges();
    }

    public void Dispose()
    {
        Context.Dispose();
    }
}

// 标记测试集合共享Fixture
[CollectionDefinition("DatabaseCollection")]
public class DatabaseCollection : ICollectionFixture<DatabaseFixture> { }

测试类实现

[Collection("DatabaseCollection")]
public class ArtistaControllerTests
{
    private readonly ArtistaController _controller;

    public ArtistaControllerTests(DatabaseFixture fixture)
    {
        // 实例化真实仓储和服务
        var repository = new ArtistaRepository(fixture.Context);
        var service = new ArtistaService(repository);
        // 注入控制器
        _controller = new ArtistaController(service);
    }

    [Fact]
    public async Task GetArtista_ReturnsValidSeedData()
    {
        var result = await _controller.GetArtista(1);
        var okResult = Assert.IsType<OkObjectResult>(result);
        var artista = Assert.IsType<Artista>(okResult.Value);
        
        Assert.NotNull(artista);
        Assert.Equal("测试艺术家1", artista.Nome);
    }
}

2. 若必须Mock服务层,需配置Mock调用真实逻辑

如果场景要求必须MockIArtistaService,需要让Mock方法指向基于真实上下文的服务实现:

[Collection("DatabaseCollection")]
public class ArtistaControllerTests
{
    private readonly ArtistaController _controller;

    public ArtistaControllerTests(DatabaseFixture fixture)
    {
        var realRepository = new ArtistaRepository(fixture.Context);
        var realService = new ArtistaService(realRepository);
        
        var mockService = new Mock<IArtistaService>();
        // 配置Mock方法调用真实服务逻辑
        mockService.Setup(s => s.ObterPorId(It.IsAny<int>()))
                   .ReturnsAsync((int id) => realService.ObterPorId(id));

        _controller = new ArtistaController(mockService.Object);
    }

    [Fact]
    public async Task GetArtista_ReturnsSeedData()
    {
        var result = await _controller.GetArtista(2);
        var okResult = Assert.IsType<OkObjectResult>(result);
        var artista = Assert.IsType<Artista>(okResult.Value);
        
        Assert.NotNull(artista);
        Assert.Equal("测试艺术家2", artista.Nome);
    }
}

3. Fixture常见问题排查

  • 确保内存库名唯一:用Guid.NewGuid().ToString()生成库名,避免不同测试共享数据导致污染。
  • 验证种子数据写入:在Fixture的SeedTestData方法中,调用SaveChanges()后检查context.Artistas.Count()是否符合预期,确认数据已写入。
  • 检查上下文配置:确保内存数据库配置覆盖了生产环境的MariaDB配置,没有被其他DI配置覆盖。

关键注意点

  • 测试控制器的端到端业务逻辑时,优先使用真实服务+内存数据库,Mock更适合隔离第三方依赖(如外部API),而非自身的服务和仓储层。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 20:03:10