Java 简化和改进数据类型映射器的单元测试

Java 简化和改进数据类型映射器的单元测试,java,unit-testing,fixtures,mapstruct,Java,Unit Testing,Fixtures,Mapstruct,我已经使用Mapstruct编写了类型映射程序,用于在实体和DTO之间映射时完成一些常见任务,例如重命名属性,通过将属性从嵌套结构中移到根级别等来“挑选”属性。初始PoC实现如下所示: @Mapper( componentModel = "spring", injectionStrategy = InjectionStrategy.CONSTRUCTOR, uses = {DocumentationMapper.class}, imports = {Kind.cl

我已经使用Mapstruct编写了类型映射程序,用于在实体和DTO之间映射时完成一些常见任务,例如重命名属性,通过将属性从嵌套结构中移到根级别等来“挑选”属性。初始PoC实现如下所示:

@Mapper(
    componentModel = "spring",
    injectionStrategy = InjectionStrategy.CONSTRUCTOR,
    uses = {DocumentationMapper.class},
    imports = {Kind.class})
public interface ConnectorMapper {
  @Mapping(source = "metadata.extendedProperties", target = "metadata.additionalProperties")
  @Mapping(source = "metadata.documentation", target = "documentation")
  @Mapping(source = "metadata.name", target = "name")
  @Mapping(source = "spec", target = "connectorSpecYaml")
  ConnectorDto fromEntity(final ConnectorYaml connectorYaml);

  @InheritInverseConfiguration
  @Mapping(target = "kind", expression = "java(Kind.Connector)")
  ConnectorYaml fromDto(final ConnectorDto connectorDto);
}
此映射器基本上执行以下操作:

  • 将嵌套属性
    metadata.extendedProperties
    重命名为
    metadata.additionalProperties
    ,反之亦然
  • Cherry为DTO选择嵌套的
    元数据.documentation
    属性,并应用
    DocumentationMapper
    中定义的映射规则(通过
    uses
    子句),反之亦然
  • 选择DTO的嵌套
    元数据.name
    属性,反之亦然
  • spec
    属性重命名为
    connectorSpecYaml
    ,反之亦然
  • 为了对这种行为进行单元测试,我不得不编写一些几乎不可读的疯狂断言:

    @Test
    public void connectorMapperFromEntity() {
      // GIVEN a connector entity with fixed values
      final var connector = FixtureBuilder.createConnectorYaml("connector");
    
      // WHEN mapping the entity to a DTO
      final var connectorDto = connectorMapper.fromEntity(connector);
    
      // THEN the mapping yields a result
      assertThat(connectorDto).isNotNull();
      // AND the name has been cherry-picked from metadata into the target
      assertThat(connectorDto.getName()).isEqualTo(connector.getMetadata().getName());
      // AND the metadata has been mapped correctly
      assertThat(connectorDto.getMetadata())
          .isEqualToIgnoringGivenFields(connector.getMetadata(), "additionalProperties");
      // AND the extended properties in metadata have been renamed correctly
      assertThat(connectorDto.getMetadata().getAdditionalProperties())
          .isEqualTo(connector.getMetadata().getExtendedProperties());
      // AND the documentation has been cherry-picked into the target
      assertThat(connectorDto.getDocumentation())
          .isEqualToIgnoringGivenFields(
              connector.getMetadata().getDocumentation(), "additionalProperties");
      // AND the extended properties in documentation have been renamed correctly
      assertThat(connectorDto.getDocumentation().getAdditionalProperties())
          .isEqualTo(connector.getMetadata().getDocumentation().getExtendedProperties());
      // AND the spec has been mapped correctly
      assertThat(connectorDto.getConnectorSpecYaml()).isEqualTo(connector.getSpec());
    }
    
    另一个方向同样难以读取,这是因为根据夹具数据手动构建DTO:

    @Test
    public void connectorMapperFromDto() {
      final var fixture = FixtureBuilder.createConnectorYaml("connector");
      final var connectorDto =
          ConnectorDto.builder()
              .connectorSpecYaml(fixture.getSpec())
              .metadata(
                  MetadataDto.builder()
                      .additionalProperties(fixture.getMetadata().getExtendedProperties())
                      .labels(fixture.getMetadata().getLabels())
                      .build())
              .documentation(
                  DocumentationDto.builder()
                      .additionalProperties(
                          fixture.getMetadata().getDocumentation().getExtendedProperties())
                      .exampleUsage(fixture.getMetadata().getDocumentation().getExampleUsage())
                      .longDescription(fixture.getMetadata().getDocumentation().getLongDescription())
                      .shortDescription(
                          fixture.getMetadata().getDocumentation().getShortDescription())
                      .exampleResponse(fixture.getMetadata().getDocumentation().getExampleResponse())
                      .build())
              .name(fixture.getMetadata().getName())
              .build();
      final var connector = connectorMapper.fromDto(connectorDto);
      assertThat(connector).isNotNull();
      assertThat(connector).isEqualTo(fixture);
    }
    

    在这一点上,我很好奇是否有其他方法可以简化测试不执行简单重命名操作的映射程序。我在考虑用JSON硬编码输入和预期输出对象,而不是手动构建和比较它们。这是一种可行的方法还是有更合适的方法?

    您可以用随机测试数据填充DTO。检查如何执行此操作

    然后您可以编写这样的测试用例(这有点伪代码,但希望它能传达我的想法):

    
    @试验
    公共无效testNonNullCases(){
    ConnectorDto controlDto=generateTestData(ConnectorDto.class);
    //来来往往
    ConnectorYaml结果能力=connectorMapper.fromDto(controlDto);
    ConnectorTo ResultTo=connectorMapper.fromEntity(resultEntity);
    //使用assertj验证它们(嵌套和全部)
    资产(ResultTo)
    .使用递归比较()
    .ignoringFields(/*要手动执行以覆盖异常的字段)*/
    .isEqualTo(controlDto);
    //执行其余的断言。
    }
    @试验
    公共无效测试用例(){
    //如果在图中省略属性(null)和一些嵌套类,我会添加一个测试用例。只是为了查看是否正确处理了null用例。您可以在生成的代码上使用jacoco(或其他覆盖率)来驱动此测试
    }
    公共静态T generateTestData(类clz){
    //从中获得灵感https://github.com/Blastman/DtoTester/pull/1
    }
    }
    
    我最终将包含快照的JSON文件存储在resources文件夹中,在测试执行期间加载这些文件,并使用Jackson进行反序列化