使用toString()在Java中进行单元测试

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

在单元测试中,根据其toString()返回的字符串测试返回值通常是个好主意吗?

例如,执行以下操作以确保返回预期的列表:

 assertEquals(someExpression.toString() ,"[a, b, c]");

在我看来,考虑因素如下:

优点:节省时间(构建实际预期值需要更长的代码)。

缺点:测试依赖于toString(),它在doc中没有正式定义,因此可以在以后的任何版本中进行更改。

java unit-testing tostring
5个回答
4
投票

我唯一一次测试对象的toString()是因为我有一个不可编辑的类没有实现hashcodeequals,而是实现了toString()来输出其字段的内容。即使这样,我也不会使用硬编码字符串作为相等测试,而是做类似的事情

SomeObject b = new SomeObject(expected, values, here);
assertEquals(a.toString(), b.toString());

你的方法最初可能节省时间,但从长远来看,维护测试需要花费更多的时间,因为你正在硬编码toString()的预期结果的字符串。

编辑1:当然,如果您正在测试输出字符串的函数/进程,那么您应该使用硬编码字符串作为预期结果。

String input = "abcde";
String result = removeVowels(input);
assertEquals(result, "bcd");

1
投票

Assert.assertThat()方法具有特定于集合的匹配器等等。

例如,contains()适用于馆藏:

List<String> someExpression = asList("a", "b", "c");
assertThat(someExpression.toString(), contains("a", "b", "c"));

优点是:

  • 你不需要实现.toString();
  • 当测试失败时,错误消息非常具有描述性,它会说“预期集合包含'b'并且没有找到它。”

尝试:

import static org.junit.Assert.assertThat;
import static org.hamcrest.Matchers.contains;
import static java.util.Arrays.asList;

List<String> someExpression = asList("a", "c");
assertThat(someExpression.toString(), contains("a", "b", "c"));

1
投票

我在特殊情况下使用toString,特别是当等效代码要复杂得多时。随着您获得更复杂的数据结构,您需要一种快速方法来测试整个结构。如果您逐个字段地编写要测试的代码,则有可能忘记添加字段。

我的例子是https://vanilla-java.github.io/2016/03/23/Microservices-in-the-Chronicle-world-Part-1.html

TopOfBookPrice tobp = new TopOfBookPrice("Symbol", 123456789000L, 1.2345, 1_000_000, 1.235, 2_000_000);
assertEquals("!TopOfBookPrice {\n" +
        "  symbol: Symbol,\n" +
        "  timestamp: 123456789000,\n" +
        "  buyPrice: 1.2345,\n" +
        "  buyQuantity: 1000000.0,\n" +
        "  sellPrice: 1.235,\n" +
        "  sellQuantity: 2000000.0\n" +
        "}\n", tobp.toString());

这个测试失败了,为什么?您可以在IDE中轻松查看它产生的内容

Display Comparison

当您获得更复杂的示例时,检查每个(嵌套)值并在以后中断时进行更正非常繁琐。更长的例子是

assertEquals("--- !!meta-data #binary\n" +
        "header: !SCQStore {\n" +
        "  wireType: !WireType BINARY,\n" +
        "  writePosition: 0,\n" +
        "  roll: !SCQSRoll {\n" +
        "    length: !int 86400000,\n" +
        "    format: yyyyMMdd,\n" +
        "    epoch: 0\n" +
        "  },\n" +
        "  indexing: !SCQSIndexing {\n" +
        "    indexCount: !short 16384,\n" +
        "    indexSpacing: 16,\n" +
        "    index2Index: 0,\n" +
        "    lastIndex: 0\n" +
        "  },\n" +
        "  lastAcknowledgedIndexReplicated: -1,\n" +
        "  recovery: !TimedStoreRecovery {\n" +
        "    timeStamp: 0\n" +
        "  }\n" +
        "}\n" +
        "# position: 344, header: 0\n" +
        "--- !!data #binary\n" +
        "msg: Hello world\n" +
        "# position: 365, header: 1\n" +
        "--- !!data #binary\n" +
        "msg: Also hello world\n", Wires.fromSizePrefixedBlobs(mappedBytes.readPosition(0)));

我有更长的例子,我这​​样做。我不需要为我检查的每个值写一行,如果格式改变,甚至我知道它的一点点。我不希望格式出乎意料地改变。

注意:我总是把预期值放在第一位。


1
投票

使用toString()在Java中进行单元测试

如果在单元测试中,您的意图是断言对象的所有字段都是等于的,则测试对象的toString()方法必须返回一个字符串,显示所有字段的键值。 但正如你强调的那样,在这个时间里,类的字段可以改变,因此你的toString()方法可能无法反映实际字段,而toString()方法也没有为它设计。

测试依赖于toString(),它在doc中没有正式定义,因此可以在以后的任何版本中进行更改。

toString()的替代方案,允许编写没有的好代码 限制你维护一个toString()方法,返回所有键值字段或混合toString()初始意图用于调试目的与断言目的。 此外,单元测试应记录测试方法的行为。 以下代码对预期行为不能自我解释:

 assertEquals(someExpression.toString() ,"[a, b, c]");

1)反思库

我们的想法是将Java反射机制与JUnit断言机制(或您使用的任何测试单元API)混合在一起。 通过反射,您可以比较两个相同类型的对象中的每个字段是否相等。这个想法如下:您构建一个错误文本消息,指示两个字段之间不遵守相等性的字段以及出于哪些原因。 当通过反射比较所有字段时,如果错误消息不为空,则使用单元测试机制抛出失败异常,其中包含您之前构建的相关错误消息。 否则,断言是成功的。结果是相关的,我经常在我的项目中使用它。 您可以自己动手,也可以使用像Unitils这样的API来为您完成工作。

User user1 = new User(1, "John", "Doe");
User user2 = new User(1, "John", "Doe");
ReflectionAssert.assertReflectionEquals(user1, user2);

就个人而言,我创建了自己的库来完成这项工作,可以在断言中添加多个自定义。这不是很难做到,你可以从Unitils激励。

2)单元测试匹配器库

我认为这是一个更好的选择。 你可以使用Harmcrest或AssertJ。 纯粹的反思更加冗长,但它也有很大的优势:

  • 它特别记录了测试方法所期望的行为。
  • 它提供流畅的方法来断言
  • 它提供了多种断言功能,以减少单元测试中的锅炉板代码

使用AssertJ,您可以替换此代码:

 assertEquals(foo.toString() ,"[value1=a, value1=b, value1=c]");

其中foo是定义为的类的Foo实例:

public class Foo{

  private String value1;
  private String value2;
  private String value3;

  // getters
}

通过以下方式:

Assertions.assertThat(someExpression)
          .extracting(Foo::getValue1, Foo::getValue2, Foo::getValue3)
          .containsExactly(a, b, c);

0
投票

最好覆盖equalshashCode(你自己或者例如使用lomboks @EqualsAndHashCode)并使用它们比使用toString进行比较。通过这种方式,您可以明确地进行有关相等性的测试,然后可以重用这些方法。

然后你可以做(​​跟你的列表示例):

assertEquals(
    someExpression,
    Arrays.asList(new Foo("a"), new Foo("b"), new Foo("c"))
);
© www.soinside.com 2019 - 2024. All rights reserved.