扫描dtos中的所有字段,并根据其实体查找缺失和额外的字段



我想创建一个单元测试,它将使用反射来查找dto中所有通过其持久性实体实现BaseDto的缺失字段。这就是我所做的。

@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);
}
}
}
}

但是问题是dto可能有一个在实体类中的字段,但是测试会认为这是一个缺失的字段。

例如:

Dto类:ContractAudit有customerId字段(customerId)。ContractEntity有一个公共的CustomerEntity customer。这是相同的领域。当然,在测试中它们是不同的。我不明白怎么忽视他们。我也不想硬编码过滤器,跳过所有以'id'前缀结尾的过滤器。

@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;
}

输出:DTO类中缺少字段:ContractAudit对于实体类:ContractEntity: [customer, deleted, documents]

DTO类中的额外字段:ContractAudit用于实体类:ContractEntity: [customerId]

我想有缺失的字段:[deleted, documents]

如果你对如何做到这一点有任何其他的想法,我很乐意听到。我不是要求执行。建议只)

哈哈。我找到了解决问题的办法。我以前的方法是不正确的。因为不可能在每种情况下都通过名称正确地找到'missing'和'extra'字段。我决定使用:

assertThat(entityClass.getDeclaredFields()).hasSameSizeAs(auditDtoClass.getDeclaredFields());

所以这段代码检查的是entityClass和DtoClass是否声明了相同数量的字段(属性)。如果没有,则测试失败并打印每个类中的所有字段。如果谁有更好的主意,我很乐意听听。

最新更新