日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

在测试中使用匹配器

發布時間:2023/12/3 编程问答 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 在测试中使用匹配器 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

我們被迫在測試代碼中寫太多斷言行的日子已經一去不復返了。 鎮上有一個新的警長:assertThat和他的代理人:匹配者。 好吧,這不是什么新東西,但是無論如何,我想向您介紹匹配器的使用方式,然后對匹配器概念進行擴展,我發現這對于為代碼開發單元測試非常有用。

首先,我將介紹匹配器的基本用法。 當然,您可以直接從其作者那里完整地了解hamcrest匹配器功能:
https://code.google.com/p/hamcrest/wiki/Tutorial 。

基本上,匹配器是定義兩個對象何時匹配的對象。 通常,第一個問題是您為什么不使用等于? 好吧,有時您不想在它們的所有字段上都匹配兩個對象,而只是在其中的某些字段上匹配,如果您使用舊代碼,則會發現equals實現不存在或不符合您的預期。 另一個原因是使用assertThat為您提供了一種更一致的“斷言”方法,并且可以說是更具可讀性的代碼。 因此,例如,而不是編寫:

int expected, actual; assertEquals(expected, actual);

你會寫

assertThat(expected, is(actual));

其中“ is”是靜態導入的org.hamcrest.core.Is.is

并沒有太大的區別……。 但是Hamcrest為您提供了許多非常有用的匹配器:

  • 對于數組和映射:hasItem,hasKey,hasValue
  • 數字:closeTo –一種指定相等性的方法,其邊距誤差大于,大于,小于…
  • 對象:nullValue,sameInstance

現在我們正在取得進步……Hamcrest匹配器的功能仍然是您可以為對象編寫自己的匹配器。 您只需要擴展BaseMatcher <T>類。 這是一個簡單的自定義匹配器的示例:

public class OrderMatcher extends BaseMatcher<Order> {private final Order expected;private final StringBuilder errors = new StringBuilder();private OrderMatcher(Order expected) {this.expected = expected;}@Overridepublic boolean matches(Object item) {if (!(item instanceof Order)) {errors.append("received item is not of Order type");return false;}Order actual = (Order) item;if (actual.getQuantity() != (expected.getQuantity())) {errors.append("received item had quantity ").append(actual.getQuantity()).append(". Expected ").append(expected.getQuantity());return false;}return true;}@Overridepublic void describeTo(Description description) {description.appendText(errors.toString());}@Factorypublic static OrderMatcher isOrder(Order expected) {return new OrderMatcher(expected);} }

與舊的斷言方法相比,這是一個全新的聯盟。

因此,這簡而言之就是Hamcrest的匹配器的用法。

但是,當我開始在現實生活中使用它時,尤其是在使用遺留代碼時,我意識到故事還有很多。 這是使用匹配器時遇到的一些問題:

  • 匹配器的結構可能非常重復且無聊。 我需要一種將DRY原理應用于匹配器代碼的方法。
  • 我需要一種統一的方式來訪問匹配器。 默認情況下,框架應選擇正確的匹配器。
  • 我需要比較引用了另一個對象的對象,這些對象應該已經與匹配器進行了比較(對象引用可以根據需要進行深入處理)
  • 我需要使用匹配器檢查對象集合,而無需迭代該集合(也可以使用數組匹配器……但我想要更多的J)
  • 我需要一個更靈活的匹配器。 例如,對于同一對象,我需要檢查一組字段,但在另一種情況下,則需要檢查另一組。 開箱即用的解決方案是為每種情況配備一個匹配器。 不喜歡那樣
  • 我使用了一些約定的匹配器層次結構克服了這些問題,并且知道哪些匹配器要應用以及比較或忽略哪個字段。 此層次結構的根是擴展BaseMatcher <T>的RootMatcher <T>。

    為了處理#1問題(重復代碼),RootMatcher類包含所有匹配器的通用代碼,例如用于檢查實際值是否為null或與預期對象具有相同類型,甚至是它們是否相同的方法。同一實例:

    public boolean checkIdentityType(Object received) {if (received == expected) {return true;}if (received == null || expected == null) {return false;}if (!checkType(received)){return false;}return true;}private boolean checkType(Object received) {if (checkType && !getClass(received).equals(getClass(expected))) {error.append("Expected ").append(expected.getClass()).append(" Received : ").append(received.getClass());return false;}return true;}

    這將簡化匹配器的編寫方式,我不必考慮null或恒等角情況; 所有這些都在根類中解決了。

    預期的對象和錯誤也位于根類中:

    public abstract class RootMatcher extends BaseMatcher {protected T expected;protected StringBuilder error = new StringBuilder("[Matcher : " + this.getClass().getName() + "] ");

    這樣,您可以在擴展RootMatcher之后立即進入match方法的實現,而對于錯誤,只需將消息放入StringBuilder中即可。 RootMatcher將處理將它們發送到JUnit框架以呈現給用戶的情況。

    對于問題2(自動查找匹配項),解決方案采用其工廠方法:

    @Factorypublic static Matcher is(Object expected) {return getMatcher(expected, true);}public static RootMatcher getMatcher(Object expected, boolean checkType) {try {Class matcherClass = Class.forName(expected.getClass().getName() + "Matcher");Constructor constructor = matcherClass.getConstructor(expected.getClass());return (RootMatcher) constructor.newInstance(expected);} catch (ClassNotFoundException | NoSuchMethodException | InvocationTargetException | InstantiationException | IllegalAccessException e) {}return (RootMatcher) new EqualMatcher(expected);}

    如您所見,factory方法嘗試使用兩種約定來找出應該返回哪個匹配器

  • 對象的匹配器具有對象名稱+字符串Matcher
  • 匹配器與要匹配的對象位于同一包中(建議位于同一包中,但在測試目錄中)
  • 使用此策略,我成功使用了一個匹配器:RootMatcher.is,它將為我提供所需的確切匹配器

    為了解決對象關系(第3個問題)的遞歸性質,在檢查對象字段時,我使用了RootManager中的方法來檢查將使用匹配器的相等性:

    public boolean checkEquality(Object expected, Object received) {String result = checkEqualityAndReturnError(expected, received);return result == null || result.trim().isEmpty();}public String checkEqualityAndReturnError(Object expected, Object received) {if (isIgnoreObject(expected)) {return null;}if (expected == null && received == null) {return null;}if (expected == null || received == null) {return "Expected or received is null and the other is not: expected " + expected + " received " + received;}RootMatcher matcher = getMatcher(expected);boolean result = matcher.matches(received);if (result) {return null;} else {StringBuilder sb = new StringBuilder();matcher.describeTo(sb);return sb.toString();}}

    但是集合(問題4)呢? 為了解決這個問題,您要做的就是為擴展RootMatcher的集合實現匹配器。

    因此,唯一剩下的問題是#5:使匹配器更加靈活,能夠告訴匹配器它應該忽略哪個字段以及應該考慮哪個字段。 為此,我介紹了“ ignoreObject”的概念。 當匹配器在模板(期望的對象)中找到對其的引用時,該對象將忽略該對象。 它是如何工作的? 首先,在RootMatcher中,我提供了用于返回任何Java類型的ignore對象的方法:

    private final static Map ignorable = new HashMap();static {ignorable.put(String.class, "%%%%IGNORE_ME%%%%");ignorable.put(Integer.class, new Integer(Integer.MAX_VALUE - 1));ignorable.put(Long.class, new Long(Long.MAX_VALUE - 1));ignorable.put(Float.class, new Float(Float.MAX_VALUE - 1));}/*** we will ignore mock objects in matchers*/private boolean isIgnoreObject(Object object) {if (object == null) {return false;}Object ignObject = ignorable.get(object.getClass());if (ignObject != null) {return ignObject.equals(object);}return Mockito.mockingDetails(object).isMock();}@SuppressWarnings("unchecked")public static M getIgnoreObject(Class clazz) {Object obj = ignorable.get(clazz);if (obj != null) {return (M) obj;}return (M) Mockito.mock(clazz);}@SuppressWarnings("unchecked")public static M getIgnoreObject(Object obj) {return (M) getIgnoreObject(obj.getClass());}

    如您所見,被忽略的對象將是被模擬的對象。 但是對于無法模擬的類(最終類),我提供了一些不太可能出現的任意固定值(可以對J進行改進)。 為此,開發人員必須使用RootMatcher中提供的equals方法:checkEqualityAndReturnError,它將檢查是否忽略了對象。 使用我去年提出的這種策略和構建器模式( http://www.javaadvent.com/2012/12/using-builder-pattern-in-junit-tests.html ),我可以輕松地對復雜的結構做出斷言賓語:

    import static […]RootMatcher.is; Order expected = OrderBuilder.anOrder().withQuantity(2).withTimestamp(RootManager.getIgnoredObject(Long.class)).withDescription(“specific description”).build() assertThat(order, is(expected);

    如您所見,我可以輕松地指定應忽略時間戳記,這使我可以將同一匹配器與要驗證的一組完全不同的字段一起使用。

    確實,此策略需要進行大量準備,使所有的構建者和匹配者成為可能。 但是,如果我們要擁有經過測試的代碼,并且要使測試成為主要關注應涵蓋的測試流程的工作,那么我們需要這樣的基礎和這些工具來幫助我們輕松地建立前提條件和建立我們的預期狀態。

    當然,可以使用注釋來改進實現,但是核心概念仍然存在。

    我希望本文能幫助您改善測試風格,如果有足夠的興趣,我會盡力將完整的代碼放在公共存儲庫中。

    謝謝。

    參考:來自Java日歷日歷博客的JCG合作伙伴 Stefan Bulzan的“ 在測試中使用匹配器” 。

    翻譯自: https://www.javacodegeeks.com/2013/12/using-matchers-in-tests.html

    創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎

    總結

    以上是生活随笔為你收集整理的在测试中使用匹配器的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。