2014年2月9日日曜日

JUnit用のマッチングするフィールドを指定できるMatcher(Iterable版)

JUnit4で使用可能なbeanの一部のフィールドだけでマッチングを行うMatcherの入力をIterableに対応させたものです。
ほとんどテストをしていないのは同様ですが、やっぱり作ったことを忘れてしまいそうなので上げておきます。
使用しているライブラリは下記の通りです。
 ・hamcrest-core-1.3.jar
 ・commons-lang3-3.2.1.jar
 ・JUnit用のマッチングするフィールドを指定できるMatcher
+各ライブラリで必要なライブラリ

IterableBeanPropertiesMatcher.java
package my.junit.matcher;

import org.hamcrest.Matcher;

/**
 * 一部のプロパティを選択してIterableの比較を行えるMatcher
 *
 * @author blog owner
 *
 * @param <T>
 *            任意のクラス
 */
class IterableBeanPropertiesMatcher<T> extends
  AbstractIterablePropertiesMatcher<T> {

 /**
  * コンストラクタ
  *
  * @param expected
  *            予想される値
  * @param properties
  *            比較対象のプロパティ
  */
 IterableBeanPropertiesMatcher(Iterable<T> expected, String... properties) {
  super();
  this.expected = expected;
  for (T bean : expected) {
   this.matchers.add(new BeanPropertiesMatcher<T>(bean, properties));
  }
 }

 /**
  * Matcherを取得します。
  *
  * @param expected
  *            予想される値
  * @param propeties
  *            比較対象のプロパティ
  * @return 一部のプロパティを選択してIterableの比較を行えるMatcher
  */
 public static <T> Matcher<Iterable<T>> containsValueOf(
   Iterable<T> expected, String... propeties) {
  return new IterableBeanPropertiesMatcher<T>(expected, propeties);
 }

}


IterableBeanIgnorePropertiesMatcher.java
package my.junit.matcher;

import org.hamcrest.Matcher;

/**
 * 一部のプロパティを無視してbean配列、コレクションの比較を行えるMatcher
 *
 * @author blog owner
 *
 * @param <T>
 *            任意のクラス
 */
class IterableBeanIgnorePropertiesMatcher<T> extends
  AbstractIterablePropertiesMatcher<T> {

 /**
  * コンストラクタ
  *
  * @param expected
  *            予想される値
  * @param properties
  *            比較対象外のプロパティ
  */
 IterableBeanIgnorePropertiesMatcher(Iterable<T> expected, String... ignoreProperties) {
  super();
  this.expected = expected;
  for (T bean : expected) {
   this.matchers.add(new BeanIgnorePropertiesMatcher<T>(bean, ignoreProperties));
  }
 }

 /**
  * Matcherを取得します。
  *
  * @param expected
  *            予想される値
  * @param ignoreProperties
  *            比較対象外のプロパティ
  * @return 一部のプロパティを無視してIterableの比較を行えるMatcher
  */
 public static <T> Matcher<Iterable<T>> containsExceptValueOf(
   Iterable<T> expected, String... ignoreProperties) {
  return new IterableBeanIgnorePropertiesMatcher<T>(expected, ignoreProperties);
 }

}


SelectIterablePropertiesMatcher.java
package my.junit.matcher;

import org.hamcrest.Matcher;


interface SelectIterablePropertiesMatcher<T> extends Matcher<Iterable<T>>{
 // 特になし
}


AbstractIterablePropertiesMatcher.java
package my.junit.matcher;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.hamcrest.Description;
import org.hamcrest.TypeSafeMatcher;

/**
 * 一部のプロパティを指定してbean配列、コレクションの比較を行えるMatcher
 *
 * @author blog owner
 *
 * @param <T>
 *            任意のクラス
 */
abstract class AbstractIterablePropertiesMatcher<T> extends
  TypeSafeMatcher<Iterable<T>> implements
  SelectIterablePropertiesMatcher<T> {

 /**
  * 予想される値毎のMatcher
  */
 protected List<SelectPropertiesMatcher<T>> matchers = new ArrayList<SelectPropertiesMatcher<T>>();

 /**
  * 予想される値
  */
 protected Iterable<T> expected = null;

 /**
  * 実際の値
  */
 protected Iterable<T> actual = null;

 /**
  * コンストラクタ
  */
 protected AbstractIterablePropertiesMatcher() {
 }

 /**
  * 比較を行います。
  *
  * @param actual
  *            実際の値
  * @return 比較結果
  */
 @Override
 public boolean matchesSafely(Iterable<T> actual) {
  this.actual = actual;
  if (expected == actual) {
   // 完全に同じもの
   return true;
  }
  if (expected == null || actual == null) {
   // 片方だけnullだから違う
   return false;
  }
  // ここからは配列やコレクション内の要素毎に比較
  Iterator<T> actualIte = actual.iterator();
  Iterator<SelectPropertiesMatcher<T>> matcherIte = matchers.iterator();
  boolean result = true;
  while (matcherIte.hasNext() & actualIte.hasNext()) {
   T actualEntry = actualIte.next();
   SelectPropertiesMatcher<T> matcher = matcherIte.next();
   if (!matcher.matches(actualEntry)) {
    result = false;
   }
  }
  if ((!matcherIte.hasNext() && !actualIte.hasNext()) == false) {
   // 要素数が合っていない場合はfalse
   return false;
  }
  // 一致しなかったMatcherがない場合はtrue
  return result;
 }

 /**
  * エラーの場合に表示する、実際の値を示す文字列を追加します。
  *
  * @param actual
  *            実際の値
  * @param description
  *            エラー時の文章
  */
 @Override
 public void describeMismatchSafely(Iterable<T> actual,
   Description mismatchDescription) {
  if (actual == null) {
   mismatchDescription.appendValue(null);
   return;
  }
  int index = -1;
  boolean first = true;
  for (SelectPropertiesMatcher<T> matcher : matchers) {
   index++;
   if (matcher.getUnmatchProperties().isEmpty()) {
    continue;
   }
   if (first) {
    first = false;
   } else {
    mismatchDescription.appendText(", ");
   }
   mismatchDescription.appendText("[" + Integer.valueOf(index) + "] ");
   matcher.describeMismatch(mismatchDescription);
  }
 }

 /**
  * エラーの場合に表示する、予測の値を示す文字列を追加します。
  *
  * @param description
  *            エラー時の文章
  */
 @Override
 public void describeTo(Description description) {
  if (actual == null) {
   description.appendValue(expected.getClass().getName());
   return;
  }
  int index = -1;
  boolean first = true;
  for (SelectPropertiesMatcher<T> matcher : matchers) {
   index++;
   if (matcher.getUnmatchProperties().isEmpty()) {
    continue;
   }
   if (first) {
    first = false;
   } else {
    description.appendText(", ");
   }
   description.appendText("[" + Integer.valueOf(index) + "] ");
   matcher.describeTo(description);
  }
 }

}

0 件のコメント:

コメントを投稿