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

XUnit中使用Moq传入映射实体时单元测试失败的修复咨询

修复XUnit+Moq单元测试中AutoMapper导致的Mock匹配失败问题

问题描述

在XUnit中使用Moq编写单元测试时,调用_sut.Create抛出异常:

Moq.MockException: 'IBaseCRUDRepository<client>.Create(client) invocation failed with mock behavior Strict. All invocations on the mock must have a corresponding setup.'

移除MockBehavior.Strict后,_sut.Create返回0而非预期的1。

测试代码

_logger = new Mock<ILogger<BaseCRUDDomainService<ClientDTO, client>>>().Object;

var mockMapper = new MapperConfiguration(cfg =>
{
    cfg.AddProfile(new DomainMapper());
});
_mapper = mockMapper.CreateMapper();

var mockRepository = new Mock<IBaseCRUDRepository<client>>(MockBehavior.Strict);
//期望返回1
var clientDTO = new ClientDTO
{
    Name = "name",
    Description = "description",
    Address = "address",
    Billing = "billing",
    CellphoneNumber = "cellphone",
    Email = "email"
};
mockRepository.Setup(r => r.Create(_mapper.Map<client>(clientDTO)))
    .Returns(1);
var _baseCRUDRepository = mockRepository.Object;
var _sut = new ClientCRUDDomainService(_logger, _mapper, _baseCRUDRepository);
var response = _sut.Create(clientDTO);

待测试方法

public int Create(clientDTO entity)
{
    var entityDB = _mapper.Map<clientDB>(entity);
    return _baseCRUDRepository.Create(entityDB);
}

ClientDTO类

public class ClientDTO
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Description { get; set; }
    public string Billing { get; set; }
    public string Address { get; set; }
    public string Email { get; set; }
    public string CellphoneNumber { get; set; }
    public int[]? Users { get; set; }
}

数据库实体client类

public partial class client
{
    public int Id { get; set; }
    public string name { get; set; } = null!;
    public string? description { get; set; }
    public string? billing { get; set; }
    public string? address { get; set; }
    public string? email { get; set; }
    public string? cellphone_number { get; set; }
}

映射器DomainMapper类

public class DomainMapper : Profile
{
    public DomainMapper() : base()
    {
        var configuration = new MapperConfiguration(cfg =>
        {
            cfg.AllowNullCollections = true;
        });

        configuration.CompileMappings();
        configuration.AssertConfigurationIsValid();

        CreateMap<ClientDTO, client>()
            .ForMember(db => db.Id, opt => opt.MapFrom(dto => dto.Id))
            .ForMember(db => db.cellphone_number, opt => opt.MapFrom(dto => dto.CellphoneNumber))
            .ReverseMap()
            .ForMember(dto => dto.Id, opt => opt.MapFrom(src => src.Id))
            .ForMember(dto => dto.CellphoneNumber, opt => opt.MapFrom(src => src.cellphone_number));
    }
}

问题原因

核心问题是Moq的默认匹配逻辑是引用相等:你在Setup时传入了_mapper.Map<client>(clientDTO)生成的具体对象实例,但在待测试方法Create内部,AutoMapper会重新生成一个全新的client对象实例。这两个实例属性值一致,但内存引用不同,Moq会判定为不匹配的调用,因此Strict模式下直接报错;非Strict模式下因无匹配的Setup,返回int类型的默认值0。

另外待测试方法存在笔误:_mapper.Map<clientDB>(entity)中的clientDB应该是client,否则映射会失败(你的数据库实体类是client)。

修复方案

方案1:匹配任意client实例(简单快捷)

如果不需要验证传入Create方法的实体属性,直接用It.IsAny<client>()匹配所有client类型的参数:

mockRepository.Setup(r => r.Create(It.IsAny<client>()))
    .Returns(1);

方案2:匹配指定属性的client实例(精准验证)

如果需要确保传入的实体属性符合预期,用It.Is<client>(x => ...)做属性匹配:

mockRepository.Setup(r => r.Create(It.Is<client>(x => 
    x.name == clientDTO.Name &&
    x.description == clientDTO.Description &&
    x.address == clientDTO.Address &&
    x.billing == clientDTO.Billing &&
    x.cellphone_number == clientDTO.CellphoneNumber &&
    x.email == clientDTO.Email
)))
.Returns(1);

额外修复:修正待测试方法的映射类型

把待测试方法中的clientDB改为client,确保映射逻辑正确:

public int Create(ClientDTO entity)
{
    var entityDB = _mapper.Map<client>(entity); // 修正clientDB为client
    return _baseCRUDRepository.Create(entityDB);
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 08:32:01