Upgrade to Pro — share decks privately, control downloads, hide ads and more …

JUnit のオブジェクト等価比較を怠けたい! #渋谷java

KOMIYA Atsushi
November 16, 2013

JUnit のオブジェクト等価比較を怠けたい! #渋谷java

第4回 渋谷java http://connpass.com/event/3744/ でお話した「JUnit のオブジェクト等価比較を怠けたい!」の発表資料です。発表で使用するはずだったデモコードは https://github.com/komiya-atsushi/shibuya-java4 こちらのリポジトリにあります。ブログエントリは http://blog.k11i.biz/2013/11/4-java-junit-matcher.html こちら。

KOMIYA Atsushi

November 16, 2013
Tweet

More Decks by KOMIYA Atsushi

Other Decks in Technology

Transcript

  1. 例例 8 /**    *  アイテムを検索索します  */   public  SearchResultDto

     search(String...  conditions)  {  ...  } /**    *  検索索結果を表します  */   public  class  SearchResultDto  {          /**  検索索条件に合致する結果の総数  */          public  Integer  numAllItems;            /**  検索索を開始した開始位置  */          public  Integer  fromIndex;            /**  取得した件数  */          public  Integer  numItemsInPage;            /**  検索索された⽇日時  */          public  Date  searchedAt;            /**  書籍情報  */          public  List<BookDto>  items;   } /**    *  書籍の情報を表します。  */   public  class  BookDto  {          /**  アイテムの識識別⼦子  */          public  Long  id;            /**  種類  */          public  Integer  type;            /**  書籍名  */          public  String  title;            /**  このオブジェクトが作成された⽇日時  */          public  Date  createdAt;            /**  詳細情報  */          public  List<DetailDto>  details;   }   ああ、#equals() が実装 されてないんだ  (´・ω・`) メソッド呼び出し時刻に 依存した値になるのね… (検証では対象外としたい)
  2. 1.  assertThat()  をひたすら並べる 10 /**    *  検索索結果を表します  */  

    public  class  SearchResultDto  {          /**  検索索条件に合致する結果の総数  */          public  Integer  numAllItems;            /**  検索索を開始した開始位置  */          public  Integer  fromIndex;            /**  取得した件数  */          public  Integer  numItemsInPage;            /**  検索索された⽇日時  */          public  Date  searchedAt;            /**  書籍情報  */          public  List<BookDto>  items;   } assertThat(actual.numAllItems,  is(100));   assertThat(actual.fromIndex,  is(20));   assertThat(actual.numItemsInPage,  is(10));     //  searchedAt  はあえて⽐比較しない     assertThat(actual.items,  hasSize(2));   assertThat(actual.items.get(0).id,  is(123));   ...   L さしみたんぽぽ作業過ぎてつらい…
  3. 2.  #equals()  を実装する 11 /**    *  検索索結果を表します  */  

    public  class  SearchResultDto  {          /**  検索索条件に合致する結果の総数  */          public  Integer  numAllItems;            /**  検索索を開始した開始位置  */          public  Integer  fromIndex;            /**  取得した件数  */          public  Integer  numItemsInPage;            /**  検索索された⽇日時  */          public  Date  searchedAt;            /**  書籍情報  */          public  List<BookDto>  items;   } public  boolean  equals(Object  o)  {          if  (!(o  instanceof  SearchResultDto))  {                  return  false;          }            SearchResultDto  other  =  (SearchResultDto)o;          if  (!this.numAllItems.equals(other.numAllItems))  {                  return  false;          }          ...   }   public  boolean  equals(Object  o)  {          if  (!(o  instanceof  SearchResultDto))  {                  return  false;          }            SearchResultDto  other  =  (SearchResultDto)o;          return  new  EqualsBuilder()                  .append(this.numAllItems,  other.numAllItems)                  ...                  .isEquals();   }   普通に実装する commons-lang  を使う
  4. /**    *  検索索結果を表します  */   public  class  SearchResultDto  {

             /**  検索索条件に合致する結果の総数  */          public  Integer  numAllItems;            /**  検索索を開始した開始位置  */          public  Integer  fromIndex;            /**  取得した件数  */          public  Integer  numItemsInPage;            /**  検索索された⽇日時  */          public  Date  searchedAt;            /**  書籍情報  */          public  List<BookDto>  items;   } public  boolean  equals(Object  o)  {          if  (!(o  instanceof  SearchResultDto))  {                  return  false;          }            SearchResultDto  other  =  (SearchResultDto)o;          if  (!this.numAllItems.equals(other.numAllItems))  {                  return  false;          }          ...   }   public  boolean  equals(Object  o)  {          if  (!(o  instanceof  SearchResultDto))  {                  return  false;          }            SearchResultDto  other  =  (SearchResultDto)o;          return  new  EqualsBuilder()                  .append(this.numAllItems,  other.numAllItems)                  ...                  .isEquals();   }   普通に実装する commons-lang  を使う 12 2.  #equals()  を実装する //  verify   assertThat(actual,  is(expected));   L でも  #equals()… J テストコードはとってもすっきり!
  5. 3. 専⽤用の  Matcher  を実装する 13 class  SearchResultDtoMatcher  extends  BaseMatcher<SearchResultDto>  {

             private  SearchResultDto  expected;          private  String  message;            @Override          public  boolean  matches(Object  item)  {                  SearchResultDto  actual  =  (SearchResultDto)  item;                                    if  (!actual.numAllItems.equals(expected.numAllItems))  {                          message  =  "numAllItems  の値が異異なります";                          return  false;                  }                  ...          }          ...   }  
  6. 3. 専⽤用の  Matcher  を実装する 14 class  SearchResultDtoMatcher  extends  BaseMatcher<SearchResultDto>  {

             private  SearchResultDto  expected;          private  String  message;            @Override          public  boolean  matches(Object  item)  {                  SearchResultDto  actual  =  (SearchResultDto)  item;                                    if  (!actual.numAllItems.equals(expected.numAllItems))  {                          message  =  "numAllItems  の値が異異なります";                          return  false;                  }                  ...          }          ...   }   J fail  したときに値が⼀一致 しない箇所を明確にできる L コード量量は  #equals()  の⽐比ではない
  7. IsEquivalentTo の概要&機能(1) • 概要 • イメージ的には、commons-lang  の   EqualsBuilder#reflectionEquals() の考えを   Matcher

     実装に応⽤用したと思えば  OK • 機能 • List / Map / 配列列 •  データ構造を再帰的にたどります •  保持しているオブジェクトが値的に等しいかを検証します • オブジェクト •  Object#equals()  をオーバーライドしていない場合に限り、 プロパティ  / public フィールドの内容を検証します(List / Map  などなら再帰的に!) • 等価⽐比較したくないプロパティ / フィールド •  Json-path  的な記法で指定できます 17
  8. IsEquivalentTo の概要&機能(1) • 概要 • イメージ的には、commons-lang  の   EqualsBuilder#reflectionEquals() の考えを   Matcher

     実装に応⽤用したと思えば  OK • 機能 • List / Map / 配列列 •  データ構造を再帰的にたどります •  保持しているオブジェクトが値的に等しいかを検証します • オブジェクト •  Object#equals()  をオーバーライドしていない場合に限り、 プロパティ  / public フィールドの内容を検証します(List / Map  などなら再帰的に!) • 等価⽐比較したくないプロパティ / フィールド •  Json-path  的な記法で指定できます 18 すべてを説明していると時間がなくなる ので、詳細はコードを御覧ください><
  9. こんなクラスを使います 21 /**    *  ワードカウント機能を提供します。  */   public  class

     WordCount  {          /**  各単語の出現頻度度を保持します  */          public  Map<String,  Integer>  wordCounts;            /**  最頻出する単語の上位3つを保持します  */          public  List<String>  top3Words;            /**  ワードカウントの処理理に要した時間(ミリ秒)を保持します  */          public  long  elapsedMillis;            /**  元の⽂文章を保持します  */          public  Date  text;   }   ここは等価⽐比較の対象外としたい