在单元测试中模拟 Automapper 是一个好习惯吗?

问题描述 投票:0回答:2

在这个代码库中,我们使用自动映射器并有 2 层,

Domain
Service
。每个都有其用于数据表示的对象,
DomainItem
ServiceItem
。该服务从域获取数据,使用构造函数注入的自动映射器实例来映射

class Service 
{
  public ServiceItem Get(int id)
  {
    var domainItem = this.domain.Get(id);
    return this.mapper.Map<DomainItem, ServiceItem>(domainItem);
  }
}

假设最佳实践,因此映射器没有副作用,也没有外部依赖项。您可以编写一个静态函数来在几秒钟内将一个对象转换为另一个对象,只需映射字段即可。

考虑到这一点,在这样的单元测试中模拟映射器是一个好习惯吗?

[TestClass]
class UnitTests
{
  [TestMethod]
  public void Test()
  {
    var expected = new ServiceItem();

    var mockDomain = new Mock<IDomain>();
    // ... setup
    var mockMapper = new Mock<IMapper>();
    mockMapper.Setup(x => x.Map<DomainItem, ServiceItem>(It.IsAny<DomainItem>()))
      .Returns(expected);


    var service = new Service(mockDomain.Object, mockMapper.Object);
    var result = service.Get(0);

    Assert.AreEqual(expected, result);
  }
}

对我来说,这样的单元测试似乎并没有真正带来任何价值,因为它只是有效地测试模拟,所以我要么根本不写它,要么我使用实际的映射器,而不是模拟的映射器。我是对的还是我忽略了什么?

c# unit-testing mocking automapper software-design
2个回答
16
投票

我认为这里的问题是测试编写得很糟糕,因为它实际上试图实现测试的目标

Service.Get()

我编写这个测试的方式如下:

[TestMethod]
public void Test()
{
  var expected = new ServiceItem();

  var mockDomain = new Mock<IDomain>();
  var expectedDomainReturn = new DomainItem(0); //Illustrative purposes only
  mockDomain.Setup(x => x.DomainCall(0)).Returns(expectedDomainReturn); //Illustrative purposes only

  var mockMapper = new Mock<IMapper>();
  mockMapper.Setup(x => x.Map<DomainItem, ServiceItem>(It.IsAny<DomainItem>()))
      .Returns(expected);    


  var service = new Service(mockDomain.Object, mockMapper.Object);
  var result = service.Get(0);

  mockDomain.Verify(x => x.DomainCall(0), Times.Once);
  mockMapper.Verify(x => x.Map<DomainItem, ServiceItem>(expectedDomainReturn), Times.Once);
}

此测试不是真正检查

service.Get()
的功能,而是根据响应检查传递的参数对于各个依赖项调用是否正确。因此,您没有测试
AutoMapper
本身,也不应该需要这样做。

检查

result
基本上没什么用,但会提高代码覆盖率。


0
投票

我知道我迟到了,但我认为模拟 Automapper 不是一个好主意,因为您可能会忘记映射类或其他一些映射错误。

例如,如果我们添加一个带有配置文件的类

public Profile()
{
    CreateMap<MyClass1, MyClass2>()
        .ForMember((MyClass2 dest) => dest.Id, (opt) => opt.MapFrom(src => src.Id))
    ...
}

您可以在测试中创建映射器

var myProfile = new Profile();
var configuration = new MapperConfiguration(cfg => 
    cfg.AddProfile(myProfile));
IMapper mapper = new Mapper(configuration);

您可以测试一下您的映射是否存在问题

mapper.ConfigurationProvider.AssertConfigurationIsValid();

如果您没有为一个属性设置转换,或者多次定义相同的转换,则会发生这种情况。

因此您可以测试您的自动映射器配置。

关于最后一个属性,另请参阅

断言配置有效自动映射器文档

© www.soinside.com 2019 - 2024. All rights reserved.