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

ASP.NET Core 6.0下如何分离DI并对自定义类型做集成测试?

在.NET 6中拆分依赖注入到独立项目并用于集成测试

核心思路

把原本写在Program.cs里的DI注册逻辑抽离到独立类库项目,让Web项目和测试项目都能复用这套配置,既保持关注点分离,又能在测试中直接构建容器获取服务实例,完全不需要依赖WebApplicationFactory。

具体实现步骤

1. 创建独立的DI配置类库

新建一个类库项目(比如命名为YourApp.DependencyInjection),在里面编写静态扩展方法封装所有服务注册逻辑:

using Microsoft.Extensions.DependencyInjection;
using AutoMapper;
using YourApp.Repositories;
using YourApp.Services;
using YourApp.Logging;

namespace YourApp.DependencyInjection
{
    public static class ServiceCollectionExtensions
    {
        public static IServiceCollection AddAppCoreDependencies(this IServiceCollection services)
        {
            // 注册自定义业务服务
            services.AddScoped<IRepository, RepositoryImpl>();
            services.AddScoped<IService1, Service1Impl>();
            services.AddScoped<IService2, Service2Impl>();

            // 注册第三方依赖
            services.AddAutoMapper(typeof(YourApp.MappingProfile));
            services.AddTransient<IAppLogger, AppLoggerImpl>();

            // 注册数据库上下文(测试时可替换为内存库)
            services.AddDbContext<AppDbContext>(options => 
                options.UseSqlServer("YourConnectionString"));

            return services;
        }
    }
}

2. Web项目中复用DI配置

在Web项目的Program.cs中,只需要调用这个扩展方法,替换原本的零散注册:

var builder = WebApplication.CreateBuilder(args);

// 复用独立类库中的DI配置
builder.Services.AddAppCoreDependencies();

// Web专属配置(如控制器、Swagger等)留在Program.cs
builder.Services.AddControllers();
builder.Services.AddSwaggerGen();

var app = builder.Build();
// ... 后续中间件配置 ...

3. 集成测试中直接构建容器

在测试项目(比如NUnit)中,引用YourApp.DependencyInjection和业务项目,手动构建ServiceCollection并生成容器,直接获取服务实例进行测试:

using NUnit.Framework;
using Microsoft.Extensions.DependencyInjection;
using YourApp.DependencyInjection;
using YourApp.Repositories;
using YourApp.Services;

namespace YourApp.Tests.Integration
{
    [TestFixture]
    public class ServiceIntegrationTests
    {
        private ServiceProvider _serviceProvider;

        [SetUp]
        public void Setup()
        {
            var services = new ServiceCollection();
            
            // 复用核心DI配置
            services.AddAppCoreDependencies();

            // 替换测试环境专属依赖(比如用内存数据库)
            services.AddDbContext<AppDbContext>(options => 
                options.UseInMemoryDatabase("TestDatabase"));

            // 构建容器
            _serviceProvider = services.BuildServiceProvider();
        }

        [Test]
        public void Service1_ShouldPerformBusinessLogicCorrectly()
        {
            // Scoped服务需在作用域内解析
            using var scope = _serviceProvider.CreateAsyncScope();
            var service1 = scope.ServiceProvider.GetRequiredService<IService1>();

            // 执行测试逻辑
            var result = service1.DoBusinessOperation();
            Assert.IsNotNull(result);
            Assert.AreEqual("ExpectedValue", result.SomeProperty);
        }

        [TearDown]
        public void TearDown()
        {
            _serviceProvider.Dispose();
        }
    }
}

4. 测试IOC容器本身的有效性

添加专门的测试用例,验证容器能成功解析所有关键服务,确保DI配置没有缺失或冲突:

[Test]
public void Container_ShouldResolveAllCriticalServices()
{
    var services = new ServiceCollection();
    services.AddAppCoreDependencies();
    var provider = services.BuildServiceProvider();

    // 逐一验证关键服务可正常解析
    Assert.DoesNotThrow(() => provider.GetRequiredService<IRepository>());
    Assert.DoesNotThrow(() => provider.GetRequiredService<IService1>());
    Assert.DoesNotThrow(() => provider.GetRequiredService<IService2>());
    Assert.DoesNotThrow(() => provider.GetRequiredService<IMapper>());
    Assert.DoesNotThrow(() => provider.GetRequiredService<IAppLogger>());
}

注意事项

  • 隔离Web专属服务:把AddControllers、AddSwaggerGen这类仅Web项目需要的注册留在Program.cs,避免DI类库耦合Web框架。
  • 测试环境替换依赖:对于数据库、外部API这类依赖,在测试中用内存实现、Mock对象或测试专用实例替换,保证测试独立且快速。
  • 作用域管理:Scoped类型的服务必须在AsyncServiceScope内解析,测试完成后记得释放容器和作用域,避免资源泄漏。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 23:48:29