我有一个包含数百个 C# 类文件的大型项目。需要为依赖于许多参数的服务功能编写 xUnit 测试,并且这些类之间可能存在依赖关系。在某些情况下,构造函数需要
ILogger<T>
。初始化这些依赖项以运行测试非常耗时。
有没有办法根据通过参数反射获得的类名来创建ILogger<T>
?
1 - 获取所需参数
public static object CreateInstance(Type type, Assembly[] assemblies)
{
try
{
var constructor = type.GetConstructors().FirstOrDefault();
if (constructor == null)
{
throw new InvalidOperationException($"No public constructor found for type {type.FullName}");
}
var parameters = constructor.GetParameters();
var parameterInstances = parameters.Select(p => ResolveDependency(p.ParameterType, assemblies)).ToArray();
return constructor.Invoke(parameterInstances);
}
catch (Exception ex)
{
throw;
}
}
2 - 解决依赖关系
private static object ResolveDependency(Type type, Assembly[] assemblies)
{
if (type.Name.IndexOf("IMapper") > -1)
{
return AutoMapperConfig.GetMapper();
}
if (type.Name.IndexOf("ILogger") > -1)
{
try
{
var typeArg = type.GetGenericArguments()[0];
var entityType = FindTypeByName(typeArg.Name, assemblies);
return LoggerFactory.CreateLogger<entityType.GetType()>();
}
catch (Exception ex1)
{
throw;
}
}
foreach (var assembly in assemblies)
{
var implementationType = assembly.GetTypes().FirstOrDefault(t => type.IsAssignableFrom(t) && !t.IsInterface && !t.IsAbstract);
if (implementationType != null)
{
return CreateInstance(implementationType, assemblies);
}
}
throw new InvalidOperationException($"No implementation found for type {type.FullName}");
}
3 - 记录器实例 - 失败
public static object CreateILogger(Type type)
{
var genericType = typeof(Logger<>).MakeGenericType(type);
return Activator.CreateInstance(genericType);
}
我的目标是对于每个服务类别,我都会致电
var myService = ServiceInitializer.Initialize(MyService);
底层函数“Initialize”将收集所需的参数并迭代到最后一个参数并初始化它们。我明确忽略除我们编写的 DLL 之外的所有 DLL。
可能我选择了错误的方法来编写 XUnit 测试,非常需要您的建议,只要记住我想摆脱初始化n个类来测试一项服务。
如果我没猜错的话,已经有图书馆在为你做这项工作了:)
让我们考虑示例设置:
public interface IService1 { }
public class Service1 : IService1 { }
public interface IService2 { }
public class Service2 : IService2 { }
public class Sut
{
private readonly IService1 service1;
private readonly IService2 service2;
public Sut(IService1 service1, IService2 service2)
{
this.service1 = service1;
this.service2 = service2;
}
};
然后,如果你想测试
Sut
,但不关心依赖关系(这是好的实践),那么你只需要模拟它们。
您可以开始创建
Mock<>
对象,但这又会很麻烦。但有 nuget 包:
它们允许“自动魔术”创建对象。带有注释的单元测试中的示例用法:
public class UnitTest1
{
// Here AutoMoqCustomization enables Fixture to create
// mocks of interfaces.
private readonly IFixture _fixture = new Fixture().Customize(new AutoMoqCustomization());
[Fact]
public async Task Test1()
{
// Will automatically crweate Sut object and pass
// mockaed interfaces to constructor
var sut = _fixture.Create<Sut>();
}
}