簡體   English   中英

使用toString()在Java中進行單元測試

[英]Using toString() for unit testing in Java

在單元測試中,根據其toString()返回的字符串測試返回值通常是個好主意嗎?

例如,執行以下操作以確保返回預期的列表:

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

在我看來,考慮因素如下:

優點:節省時間(構建實際預期值需要更長的代碼)。

缺點:測試依賴於toString(),它在doc中沒有正式定義,因此可以在以后的任何版本中進行更改。

我唯一一次測試對象的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");

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"));

我在特定情況下使用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中輕松查看它產生的內容

顯示比較

當您獲得更復雜的示例時,檢查每個(嵌套)值並在以后中斷時進行更正非常繁瑣。 更長的例子是

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

我有更長的例子,我這​​樣做。 我不需要為我檢查的每個值寫一行,如果格式改變,甚至我知道它的一點點。 我不希望格式出乎意料地改變。

注意:我總是把預期值放在第一位。

使用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);

最好覆蓋equalshashCode (你自己或者使用lomboks @EqualsAndHashCode )並使用它們比使用toString進行比較。 通過這種方式,您可以明確地進行有關相等性的測試,然后可以重用這些方法。

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

assertEquals(
    someExpression,
    Arrays.asList(new Foo("a"), new Foo("b"), new Foo("c"))
);

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM