如何在单元测试中从 Automapper 模拟 IValueResolver 的依赖关系
How to mock dependencies for IValueResolver from Automapper in unit tests
在之后我有一个简单的实现IValueResolver
public class FileLinkResolver : IValueResolver<Configuration, ConfigurationDto, string>
{
private readonly IFileStorage _fileStorage;
public FileLinkResolver(IFileStorage fileStorage)
{
_fileStorage = fileStorage;
}
public string Resolve(Configuration source, ConfigurationDto destination, string destMember, ResolutionContext context)
{
return _fileStorage.GetShortTemporaryLink(source.Path);
}
}
和简单的映射配置文件
public class MappingProfile : Profile
{
public MappingProfile()
{
CreateMap<Configuration, ConfigurationDto>()
.ForMember(dest => dest.FilePath, opt => opt.MapFrom<FileLinkResolver>());
}
}
对于生产,它在以下设置时按预期工作
services.AddTransient<IFileStorage>(...);
services.AddAutoMapper();
使用 然后在控制器中注入 IMapper
。
在单元测试中我尝试存根映射器
var mapperStub = new Mapper(new MapperConfiguration(map => map.AddProfile(new MappingProfile())));
当我 运行 测试方法 应该 return 映射 dto 时,我得到
AutoMapper.AutoMapperMappingException : Error mapping types.
Mapping types:
Configuration -> ConfigurationDto
DataAccess.Models.Configuration -> Dto.ConfigurationDto
Type Map configuration:
Configuration -> ConfigurationDto
DataAccess.Models.Configuration -> Dto.ConfigurationDto
Destination Member:
FilePath
---- System.MissingMethodException : No parameterless constructor defined for this object.
我尝试将无参数构造函数添加到 FileLinkResolver
但是 NullReferenceException
问题是:如何解决 ValueResolver 的依赖关系
在当前示例中,映射器在测试时无法解析 IFileStorage
依赖关系。
更改映射器的创建方式以更接近 运行 时的创建方式。
IServiceCollection services = new ServiceCollection();
//mocking service using MOQ
var mock = Mock.Of<IFileStorage>(_ =>
_.GetShortTemporaryLink(It.IsAny<string>()) == "fake link"
);
//adding mock to service collection.
services.AddTransient<IFileStorage>(sp => mock);
//adding auto mapper with desired profile;
services.AddAutoMapper(typeof(MappingProfile));
//...add other dependencies as needed to service collection.
//...
//build provider
IServiceProvider serviceProvider = service.BuilderServiceProvider();
//resolve mapper
IMapper mapperStub = serviceProvider.GetService<IMapper>();
//Or resolve subject under test where mapper is to be injected
SubjectClass subject = serviceProvider.GetService<SubjectClass>();
//assuming ctr: public SubjectClass(IMapper mapper, .....) { ... }
从技术上讲,这并不是在模拟值解析器。它模拟解析器的依赖关系,并使用配置文件中的实际解析器。但这应该在测试目标时提供所需的行为。
在IValueResolver
public class FileLinkResolver : IValueResolver<Configuration, ConfigurationDto, string>
{
private readonly IFileStorage _fileStorage;
public FileLinkResolver(IFileStorage fileStorage)
{
_fileStorage = fileStorage;
}
public string Resolve(Configuration source, ConfigurationDto destination, string destMember, ResolutionContext context)
{
return _fileStorage.GetShortTemporaryLink(source.Path);
}
}
和简单的映射配置文件
public class MappingProfile : Profile
{
public MappingProfile()
{
CreateMap<Configuration, ConfigurationDto>()
.ForMember(dest => dest.FilePath, opt => opt.MapFrom<FileLinkResolver>());
}
}
对于生产,它在以下设置时按预期工作
services.AddTransient<IFileStorage>(...);
services.AddAutoMapper();
使用 然后在控制器中注入 IMapper
。
在单元测试中我尝试存根映射器
var mapperStub = new Mapper(new MapperConfiguration(map => map.AddProfile(new MappingProfile())));
当我 运行 测试方法 应该 return 映射 dto 时,我得到
AutoMapper.AutoMapperMappingException : Error mapping types.
Mapping types: Configuration -> ConfigurationDto DataAccess.Models.Configuration -> Dto.ConfigurationDto Type Map configuration: Configuration -> ConfigurationDto DataAccess.Models.Configuration -> Dto.ConfigurationDto Destination Member: FilePath ---- System.MissingMethodException : No parameterless constructor defined for this object.
我尝试将无参数构造函数添加到 FileLinkResolver
但是 NullReferenceException
问题是:如何解决 ValueResolver 的依赖关系
在当前示例中,映射器在测试时无法解析 IFileStorage
依赖关系。
更改映射器的创建方式以更接近 运行 时的创建方式。
IServiceCollection services = new ServiceCollection();
//mocking service using MOQ
var mock = Mock.Of<IFileStorage>(_ =>
_.GetShortTemporaryLink(It.IsAny<string>()) == "fake link"
);
//adding mock to service collection.
services.AddTransient<IFileStorage>(sp => mock);
//adding auto mapper with desired profile;
services.AddAutoMapper(typeof(MappingProfile));
//...add other dependencies as needed to service collection.
//...
//build provider
IServiceProvider serviceProvider = service.BuilderServiceProvider();
//resolve mapper
IMapper mapperStub = serviceProvider.GetService<IMapper>();
//Or resolve subject under test where mapper is to be injected
SubjectClass subject = serviceProvider.GetService<SubjectClass>();
//assuming ctr: public SubjectClass(IMapper mapper, .....) { ... }
从技术上讲,这并不是在模拟值解析器。它模拟解析器的依赖关系,并使用配置文件中的实际解析器。但这应该在测试目标时提供所需的行为。