namespace Dpz.Core.ServiceTest;
[TestFixture]
public class ServiceDependencyInjectionTest
{
[Test]
public void AddDefaultServicesRegistersGeneratedRepositoryServices()
{
var services = new ServiceCollection();
new ConfigurationBuilder().Build();
services.AddDefaultServices();
AssertScopedRegistration<IAccountService, AccountService>(services);
AssertScopedRegistration<MongoMessageOutboxStore>(services);
AssertGeneratedScopedRegistration<IMongoMessageOutboxStore>(services);
AssertScopedRegistration<HealthCheckService>(services);
AssertGeneratedScopedRegistration<IHealthCheckService>(services);
AssertGeneratedScopedRegistration<IAppOptionService>(services);
Assert.That(
System.Linq.Enumerable.Any(
services,
service =>
service.ServiceType == typeof(ISteamGameService)
&& service.Lifetime == ServiceLifetime.Transient
),
Is.True
);
}
[Test]
public void SourceGeneratorCreatesRobotsCacheMetadataForAppOptionService()
{
var metadataType = typeof(AppOptionService).Assembly.GetType(
"Dpz.Core.Service.RepositoryServiceImpl.GeneratedAppOptionServiceCacheMetadata"
);
Assert.That(metadataType, Is.Not.Null);
var buildRobotsCacheKey = metadataType!.GetMethod(
"BuildGetRobotsAsyncCacheKey",
System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static
);
Assert.That(buildRobotsCacheKey, Is.Not.Null);
Assert.That(
buildRobotsCacheKey!.Invoke(null, []),
Is.EqualTo($"{typeof(AppOptionService).FullName}:GetRobotsAsync")
);
}
[Test]
public void SourceGeneratorCreatesCacheMetadataForArticleService()
{
var metadataType = typeof(ArticleService).Assembly.GetType(
"Dpz.Core.Service.RepositoryServiceImpl.GeneratedArticleServiceCacheMetadata"
);
Assert.That(metadataType, Is.Not.Null);
var defaultPrefix = metadataType!
.GetField(
"DefaultPrefix",
System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static
)
?.GetRawConstantValue();
Assert.That(defaultPrefix, Is.EqualTo(typeof(ArticleService).FullName));
var methodTag = metadataType
.GetMethod(
"GetMethodTag",
System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static
)
?.Invoke(null, [nameof(ArticleService.GetTopArticlesAsync)]);
Assert.That(
methodTag,
Is.EqualTo(
$"{typeof(ArticleService).FullName}:{nameof(ArticleService.GetTopArticlesAsync)}"
)
);
}
private static void AssertScopedRegistration<TImplementation>(IServiceCollection services)
{
Assert.That(
System.Linq.Enumerable.Any(
services,
service =>
service.ServiceType == typeof(TImplementation)
&& service.ImplementationType == typeof(TImplementation)
&& service.Lifetime == ServiceLifetime.Scoped
),
Is.True
);
}
private static void AssertScopedRegistration<TService, TImplementation>(
IServiceCollection services
)
{
Assert.That(
System.Linq.Enumerable.Any(
services,
service =>
service.ServiceType == typeof(TService)
&& service.ImplementationType == typeof(TImplementation)
&& service.Lifetime == ServiceLifetime.Scoped
),
Is.True
);
}
private static void AssertGeneratedScopedRegistration<TService>(IServiceCollection services)
{
Assert.That(
System.Linq.Enumerable.Any(
services,
service =>
service.ServiceType == typeof(TService)
&& service.ImplementationType?.Name.StartsWith("GeneratedCached") == true
&& service.Lifetime == ServiceLifetime.Scoped
),
Is.True
);
}
}
⚠⚠ 以下内容为AI分析的结果,请根据实际情况进行判断。
代码解释
这是一个使用 NUnit 测试框架编写的单元测试类,主要用于测试 依赖注入(DI)服务注册 的正确性。
整体结构
命名空间: Dpz.Core.ServiceTest
测试类: ServiceDependencyInjectionTest
测试框架: NUnit ([TestFixture] 和 [Test] 特性)
测试方法详解
1. AddDefaultServicesRegistersGeneratedRepositoryServices()
目的: 验证 AddDefaultServices() 扩展方法是否正确注册了各种服务。
验证内容:
- ✅
IAccountService和AccountService注册为 Scoped 生命周期 - ✅
MongoMessageOutboxStore注册为 Scoped - ✅
IMongoMessageOutboxStore注册为自动生成的 Scoped 服务(通过源代码生成器) - ✅
HealthCheckService和其接口IHealthCheckService注册正确 - ✅
IAppOptionService注册为源生成的缓存服务 - ✅
ISteamGameService注册为 Transient 生命周期
2. SourceGeneratorCreatesRobotsCacheMetadataForAppOptionService()
目的: 验证源代码生成器是否为 AppOptionService 创建了缓存元数据类。
验证内容:
- ✅ 确认生成了名为
GeneratedAppOptionServiceCacheMetadata的类型 - ✅ 该类型包含静态方法
BuildGetRobotsAsyncCacheKey - ✅ 该方法返回的缓存键格式为:
"{FullTypeName}:GetRobotsAsync"
示例输出: "Dpz.Core.Service.AppOptionService:GetRobotsAsync"
3. SourceGeneratorCreatesCacheMetadataForArticleService()
目的: 验证源代码生成器为 ArticleService 创建的缓存元数据。
验证内容:
- ✅ 确认生成了
GeneratedArticleServiceCacheMetadata类型 - ✅ 包含常量字段
DefaultPrefix,值为完整类型名称 - ✅ 包含静态方法
GetMethodTag(methodName),返回格式为:"{TypeName}:{MethodName}"
示例输出: "Dpz.Core.Service.ArticleService:GetTopArticlesAsync"
辅助断言方法
AssertScopedRegistration<TImplementation>
验证某个实现类是否以 Scoped 生命周期注册(服务类型和实现类型相同)。
AssertScopedRegistration<TService, TImplementation>
验证接口-实现对是否以 Scoped 生命周期正确注册。
AssertGeneratedScopedRegistration<TService>
验证某个接口是否注册了以 "GeneratedCached" 开头的源生成实现类,且为 Scoped 生命周期。
关键技术点
- 依赖注入生命周期验证: Scoped、Transient
- 源代码生成器验证: 通过反射检查生成的类型和方法
- 缓存键生成策略: 使用
"{TypeFullName}:{MethodName}"格式 - 元数据驱动设计: 为缓存功能自动生成元数据类
典型使用场景
这类测试通常用于:
- ✅ 确保 DI 容器配置正确
- ✅ 验证源生成器(Source Generator)输出
- ✅ 防止服务注册遗漏或生命周期配置错误
- ✅ 确保缓存基础设施正确生成
AI 正在分析代码…
评论加载中...