この質問は近いですが、それでも私が望むものではありません。2つのBeanオブジェクトが同等であることを一般的な方法で主張したいと思います。そうでない場合は、ブール値の「等しい」または「等しくない」の代わりに、違いを説明する詳細なエラーメッセージが必要です。
12 に答える
unitilsライブラリを使用することをお勧めします。
http://www.unitils.org/tutorial-reflectionassert.html
パブリック クラス ユーザー { プライベートロングID; プライベート文字列が最初。 プライベート文字列が最後。 public User(long id, String first, String last) { this.id = id; this.first = 最初; this.last = 最後; } }
User user1 = new User(1, "John", "Doe"); User user2 = new User(1, "John", "Doe"); assertReflectionEquals(user1, user2);
以下も参照してください。
Commons Langを使用ToStringBuilder
して、両方を読み取り可能な文字列に変換してから、両方の文字列で使用できますassertEquals()
。
XMLが好きな場合は、java.lang.XMLEncoderを使用してBeanをXMLに変換し、2つのXMLドキュメントを比較できます。
個人的にはToStringBuilder
、フォーマットをより細かく制御でき、セット内の要素を並べ替えてフォールスネガティブを回避するなどのことができるので、私は好みます。
比較しやすくするために、Beanの各フィールドを異なる行に配置することをお勧めします(詳細については、私のブログを参照してください)。
次のようにすべてのフィールドを設定できます。
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.allOf;
import static org.hamcrest.beans.HasPropertyWithValue.hasProperty;
import static org.hamcrest.Matchers.is;
@Test
public void test_returnBean(){
arrange();
MyBean myBean = act();
assertThat(myBean, allOf(hasProperty("id", is(7L)),
hasProperty("name", is("testName1")),
hasProperty("description", is("testDesc1"))));
}
最も一般的なアプローチは、Beanメンバーを反映し、それらが1つずつ等しいかどうかをテストすることだと思います。Common langEqualsBuilder
は良いスタートであり、それを(ソースレベルで)要件に適合させる(等しい結果を返す代わりに違いを報告する)ことは大したことではないはずです。
単体テストの場合、これは ReflectionEquals を使用して JUnit と Mockito で実行できます。次の方法で実装すると、フィールドが等しくない場合にオブジェクトの JSON 表現がダンプされ、問題のある違いを簡単に見つけることができます。
import static org.junit.Assert.assertThat;
import org.mockito.internal.matchers.apachecommons.ReflectionEquals;
assertThat("Validating field equivalence of objects", expectedObjectValues, new ReflectionEquals(actualObjectValues));
あなたは実際には平等を主張しているのではなく、もっと「相違」をしているのです。明らかに、「同じ」の意味は各タイプの特定のロジックに依存し、違いの表現も異なる場合があります。この要件と従来のequals()との大きな違いの1つは、通常、equals()は最初の違いが見られるとすぐに停止するため、すべてのフィールドを続けて比較する必要があることです。
いくつかのequals()パターンを再利用することを検討しますが、独自のコードを作成する必要があると思います。
(上記のAndreas_Dへの私のコメントに基づいて)
/** Asserts two objects are equals using a reflective equals.
*
* @param message The message to display.
* @param expected The expected result.
* @param actual The actual result
*/
public static void assertReflectiveEquals(final String message,
final Object expected, final Object actual) {
if (!EqualsBuilder.reflectionEquals(expected, actual)) {
assertEquals(message,
reflectionToString(expected, ToStringStyle.SHORT_PREFIX_STYLE),
reflectionToString(actual, ToStringStyle.SHORT_PREFIX_STYLE));
fail(message + "expected: <" + expected + "> actual: <" + actual + ">");
}
}
これは私が使用しているものであり、すべての基本的な要件を満たしていると思います。リフレクティブToStringでアサートを実行することにより、Eclipseは違いを強調します。
Hamcrest ははるかに優れたメッセージを提供できますが、これにはかなり少ないコードが含まれます。
ここでは、両方の Bean が同じタイプであると想定しています。この場合、メンバー変数の値のみが Bean インスタンス間で異なります。
BeanAssertEquals などと呼ばれる util クラス (プライベート ctor を持つ public static final) を定義します。Java リフレクションを使用して、各 Bean の各メンバー変数の値を取得します。次に、異なる Bean の同じメンバー変数の値の間で equals() を実行します。等式が失敗した場合は、フィールド名を記載してください。
注: メンバー変数は通常プライベートであるため、リフレクションを使用してプライベート メンバーのアクセシビリティを一時的に変更する必要があります。
さらに、アサーションをどの程度細かく動作させたいかによって、次の点を考慮する必要があります。
Bean クラスではなく、すべてのスーパークラスのメンバー変数の等価性。
メンバー変数が配列型の場合の、配列内の要素の等価性。
複数の Bean にまたがる特定のメンバーの 2 つの値については、value1.equals(value2) の代わりに BeanAssertEquals.assertEquals(value1, value2) を実行することを検討できます。
参照した質問の回答が気に入らなかったのでtoXml
、各 Bean にメソッドを入れて、それらを xml ファイルに変換し、xmlUnit を使用して比較してみませんか。
ここで xml ファイルの比較に関する詳細情報を取得できます。
私が尋ねなければならない最初の質問は、ビーンで「深い」等しいことをしたいですか?テストする必要のある子Beanがありますか?equalsメソッドをオーバーライドできますが、これはブール値のみを返すため、「コンパレータ」を作成すると、等しくないものに関するメッセージを含む例外がスローされる可能性があります。
次の例では、equalsメソッドを実装するいくつかの方法をリストしました。
それらが同じオブジェクトインスタンスであるかどうかを確認したい場合は、Objectの通常のequalsメソッドが教えてくれます。
objectA.equals(objectB);
オブジェクトのすべてのメンバー変数がそれらを等しくすることを確認するためにcustomerequalsメソッドを記述したい場合は、次のようにequalsメソッドをオーバーライドできます...
/**
* Method to check the following...
* <br>
* <ul>
* <li>getTitle</li>
* <li>getInitials</li>
* <li>getForename</li>
* <li>getSurname</li>
* <li>getSurnamePrefix</li>
* </ul>
*
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj)
{
if ( (!compare(((ICustomer) obj).getTitle(), this.getTitle()))
|| (!compare(((ICustomer) obj).getInitials(), this.getInitials()))
|| (!compare(((ICustomer) obj).getForename(), this.getForename()))
|| (!compare(((ICustomer) obj).getSurname(), this.getSurname()))
|| (!compare(((ICustomer) obj).getSurnamePrefix(), this.getSurnamePrefix()))
|| (!compare(((ICustomer) obj).getSalutation(), this.getSalutation())) ){
return false;
}
return true;
}
最後のオプションは、Javaリフレクションを使用して、equalsメソッドのすべてのメンバー変数をチェックすることです。これは、Beanのget/setメソッドを介してすべてのメンバー変数を本当にチェックしたい場合に最適です。2つのオブジェクトのテストが同じである場合、プライベートメンバーの変数をチェックすることはできません(私は思いません)。(オブジェクトモデルに循環依存性がある場合は、これを行わないでください。返されません)
注:これは私のコードではありません、それはから来ています...
Java Reflection equals public static boolean equals(Object bean1、Object bean2){//(bean1 == bean2)がtrueを返す場合、些細なケースを処理します。
if (bean1 == null)
return false;
if (bean2 == null)
return false;
// Get the class of one of the parameters
Class clazz = bean1.getClass();
// Make sure bean1 and bean2 are the same class
if (!clazz.equals(bean2.getClass()))
{
return false;
}
// Iterate through each field looking for differences
Field[] fields = clazz.getDeclaredFields();
for (int i = 0; i < fields.length; i++)
{
// setAccessible is great (encapsulation
// purists will disagree), setting to true
// allows reflection to have access to
// private members.
fields[i].setAccessible(true);
try
{
Object value1 = fields[i].get(bean1);
Object value2 = fields[i].get(bean2);
if ((value1 == null && value2 != null) ||
(value1 != null && value2 == null))
{
return false;
}
if (value1 != null &&
value2 != null &&
!value1.equals(value2))
{
return false;
}
}
catch (IllegalArgumentException e)
{
e.printStackTrace();
}
catch (IllegalAccessException e)
{
e.printStackTrace();
}
}
return true;
これは違いの理由を教えてくれませんが、等しくないセクションを見つけたときにLog4Jへのメッセージを介して行うことができます。