首页 > 解决方案 > JUnit 避免重复断言

问题描述

我正在编写将实体转换为 DTO 的简单测试用例,反之亦然。问题更多是关于设计。在下面的代码中留下重复项是否可以接受,或者为这个断言创建外部方法更好?由于我是 Java 新手,有人可以给我提示一下吗?任何通用方法?我不想为这种简单的实体及其 DTO 使用继承或任何其他抽象,因为将有更多的代码,而不仅仅是几行重复的代码。这是它现在的样子:

@Test
void addressToAddressDTO() {
    Address address = getAddress();

    AddressDTO addressDTO = addressMapper.addressToAddressDTO(address);

    assertAll("Check if values were properly bound",
            () -> {
                assertEquals(address.getCity(), addressDTO.getCity());
                assertEquals(address.getUserDetails().getFirstName(), addressDTO.getUserDetails().getFirstName());
                assertEquals(address.getUserDetails().getUser().getUsername(), addressDTO.getUserDetails().getUser().getUsername());
                assertEquals(address.getUserDetails().getContact().getEmail(), addressDTO.getUserDetails().getContact().getEmail());
                assertEquals(address.getUserDetails().getProfileImage().getImageUrl(), addressDTO.getUserDetails().getProfileImage().getImageUrl());
            });
}

@Test
void addressDTOtoAddress() {
    AddressDTO addressDTO = getAddressDTO();

    Address address = addressMapper.addressDTOtoAddress(addressDTO);

    assertAll("Check if values were properly bound",
            () -> {
                assertEquals(addressDTO.getCity(), address.getCity());
                assertEquals(addressDTO.getUserDetails().getFirstName(), address.getUserDetails().getFirstName());
                assertEquals(addressDTO.getUserDetails().getUser().getUsername(), address.getUserDetails().getUser().getUsername());
                assertEquals(addressDTO.getUserDetails().getContact().getEmail(), address.getUserDetails().getContact().getEmail());
                assertEquals(addressDTO.getUserDetails().getProfileImage().getImageUrl(), address.getUserDetails().getProfileImage().getImageUrl());
            });
}

我的想法是创建更通用的东西,例如:

private<T, S> void assertObject(T expected, S actual) {
        assertAll("Check if values were properly bound",
                () -> {
                    assertEquals(expected.getCity(), actual.getCity());
                    assertEquals(expected.getUserDetails().getFirstName(), actual.getUserDetails().getFirstName());
                    assertEquals(expected.getUserDetails().getUser().getUsername(), actual.getUserDetails().getUser().getUsername());
                    assertEquals(expected.getUserDetails().getContact().getEmail(), actual.getUserDetails().getContact().getEmail());
                    assertEquals(expected.getUserDetails().getProfileImage().getImageUrl(), actual.getUserDetails().getProfileImage().getImageUrl());
                });
    }

但是,即使它们是相同的对象,它们也没有任何共同之处。如何实现地址和地址DTO既可以是实际的也可以是预期的hmm可互换的东西?

编辑

根据 Aaron Digulla 的回答,我做了一些更改,希望它能帮助有同样疑问的人。如果有人知道任何其他选项,请在评论部分发表。

@Test
void addressToAddressDTO() {
    Address expected = getAddress();

    AddressDTO actual = addressMapper.addressToAddressDTO(expected);

    assertEquals(
            mergeAddressDataToString(expected),
            actual.getCity() + "," +
                    actual.getUserDetails().getFirstName() + "," +
                    actual.getUserDetails().getUser().getUsername() + "," +
                    actual.getUserDetails().getContact().getEmail() + "," +
                    actual.getUserDetails().getProfileImage().getImageUrl()

    );
}

@Test
void addressDTOtoAddress() {
    AddressDTO expected = getAddressDTO();

    Address actual = addressMapper.addressDTOtoAddress(expected);

    assertEquals(
            expected.getCity() + "," +
                    expected.getUserDetails().getFirstName() + "," +
                    expected.getUserDetails().getUser().getUsername() + "," +
                    expected.getUserDetails().getContact().getEmail() + "," +
                    expected.getUserDetails().getProfileImage().getImageUrl(),
            mergeAddressDataToString(actual)
    );
}

private String mergeAddressDataToString(Address address) {
    StringJoiner stringJoiner = new StringJoiner(",");
    stringJoiner.add(address.getCity());
    stringJoiner.add(address.getUserDetails().getFirstName());
    stringJoiner.add(address.getUserDetails().getUser().getUsername());
    stringJoiner.add(address.getUserDetails().getContact().getEmail());
    stringJoiner.add(address.getUserDetails().getProfileImage().getImageUrl());

    return stringJoiner.toString();
}

标签: unit-testinggenericsjunitjunit5

解决方案


toString()当多个测试需要共享它时,我通常会为测试中的对象或测试实用程序类编写自定义方法。这样,我可以使用单个assertEquals().

我可以使用换行符使断言在 IDE 中更具可读性:

assertEquals(
    "city=Foo\n" + 
    "firstName=John\n" + 
    "user=doe\n" + 
    ....
    , toString(actual));

当您必须检查值列表时,它也可以很好地工作:

    ...
    , list.stream().map(this::toString).collect(Collectors.joining("\n---\n"));

这里最大的优势是你可以一次得到所有的不匹配。您还可以调整toString()处理极端情况的方法(例如仅比较大型列表的元素数量或舍入小数)。

它还使代码易于理解。在许多情况下,它还可以节省您编写代码以填充expected您需要的所有对象的时间。当多个测试应该产生相同的结果时,测试甚至可以共享预期的字符串。

当测试因输出更改而中断时,我可以选择整个字符串并将其替换为新输出。IDE 将为我进行格式化。


推荐阅读