samedi 7 janvier 2023

Scan all fields in dtos and find missing and extra fields by their Entities

I want to create a unit test that will use reflection to find all missing fields in dto that implement BaseDto by their persistence entities. This is what I did.

@Slf4j
public class EntityAuditDtoTest {
    @Test
    public void find_MissingAndExtraFieldsThatUsedInAuditDtosByEntity_ReturnMissingAndExtraFields() throws ClassNotFoundException {
        // Arrange
        ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false);
        scanner.addIncludeFilter(new AnnotationTypeFilter(AuditEntityType.class));

        // Find all classes annotated with @AuditEntityType in the package com.example.dto
        Set<BeanDefinition> auditDtoBeans = scanner.findCandidateComponents("com.example.dto");

        // Act
        for (BeanDefinition auditDtoBean : auditDtoBeans) {
            Class<?> auditDtoClass = Class.forName(auditDtoBean.getBeanClassName());

            // Make sure the DTO class implements BaseAuditDto
            if (!BaseAuditDto.class.isAssignableFrom(auditDtoClass)) {
                continue;
            }

            Class<?> entityClass = getEntityClassForDto(auditDtoClass);

            Field[] dtoFields = auditDtoClass.getDeclaredFields();
            Field[] entityFields = entityClass.getDeclaredFields();

            List<String> missingFields = Arrays.stream(entityFields).map(Field::getName)
                    .filter(field -> Arrays.stream(dtoFields).noneMatch(f -> f.getName().equals(field))).toList();

            if (!missingFields.isEmpty()) {
                log.error("Missing fields in DTO class: {} \nfor entity class: {} : {}", auditDtoClass.getName(),
                        entityClass.getName(), missingFields);
            }

            List<String> extraFields = Arrays.stream(dtoFields).map(Field::getName)
                    .filter(field -> Arrays.stream(entityFields).noneMatch(f -> f.getName().equals(field))).toList();

            if (!extraFields.isEmpty()) {
                log.error("Extra fields in DTO class: {} \nfor entity class: {} : {}", auditDtoClass.getName(),
                        entityClass.getName(), extraFields);
            }
        }
    }
}

But the problem is that the dto may have a field that is in the entity class, but the test will think that this is a missing field.

For example:

Dto class: ContractAudit has customerId field (customerId). And ContractEntity has public CustomerEntity customer. This is the same fields. But of course for test they are different. I don't understand how to ignore them. I also don't want to hardcode filter that skip all endings with 'id' prefix.

@Data
@AuditEntityType("Contract")
public class ContractAudit implements BaseAuditDto {
  private Long id;
  private String ref;
  private String status;
  private Long customerId;
}


@Entity
@Table(name = "contract")
@Getter
@Setter
@ToString
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class ContractEntity {
  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  @Column(name = "id")
  @ToString.Include
  private Long id;

  @Column(name = "ref", updatable = true)
  @ToString.Include
  private String ref;

  @Column(name = "status")
  @ToString.Include
  @Enumerated(value = EnumType.STRING)
  private ContractStatusEnum status;

  @ManyToOne
  @JoinColumn(name = "customer_id")
  public CustomerEntity customer;

  @Column(name = "deleted")
  @ToString.Include
  private boolean deleted;

  @OneToMany(fetch = FetchType.LAZY)
  @JoinColumn(name = "contract_id")
  private List<ContractDocumentEntity> documents;
}

Output: Missing fields in DTO class: ContractAudit for entity class: ContractEntity : [customer, deleted, documents]

Extra fields in DTO class: ContractAudit for entity class: ContractEntity : [customerId]

I want to have missing fields: [deleted, documents]

If you have any other ideas on how to do this, I'd love to hear it. I am not asking for implementation. Suggestions only)





Aucun commentaire:

Enregistrer un commentaire