Soru Eşit yöntem olmadan iki sınıfa eşitliği nasıl doğrularım?


Kaynağa sahip olmayan equals () yöntemiyle bir sınıfım olduğunu varsayalım. O sınıfın iki örneğinde eşitliği ileri sürmek istiyorum.

Birden fazla assier yapabilirim:

assertEquals(obj1.getFieldA(), obj2.getFieldA());
assertEquals(obj1.getFieldB(), obj2.getFieldB());
assertEquals(obj1.getFieldC(), obj2.getFieldC());
...

Bu çözümü sevmiyorum çünkü erken bir iddia başarısız olursa tam eşitlik resmini alamıyorum.

Kendi başıma elle karşılaştırma yapabilir ve sonucu takip edebilirim:

String errorStr = "";
if(!obj1.getFieldA().equals(obj2.getFieldA())) {
    errorStr += "expected: " + obj1.getFieldA() + ", actual: " + obj2.getFieldA() + "\n";
}
if(!obj1.getFieldB().equals(obj2.getFieldB())) {
    errorStr += "expected: " + obj1.getFieldB() + ", actual: " + obj2.getFieldB() + "\n";
}
...
assertEquals("", errorStr);

Bu bana tam eşitlik resmini veriyor, ama rahatsız edici (ve olası sıfır problemleri bile hesaba katmadım). Üçüncü bir seçenek, Karşılaştırıcı'yı kullanmaktır, ancak compareTo () hangi alanların eşitliği başarısız olduğunu bana söylemez.

Nesneden istediğim şeyi almak için, alt sınıflar ve eşitleme notları olmadan (ugh) daha iyi bir uygulama var mı?


55
2017-08-27 18:15


Menşei


Sizin için derin karşılaştırma yapan bir kütüphane mi arıyorsunuz? derin eşittir stackoverflow.com/questions/1449001/...? - Vikdor
Neden iki örneğin neden eşit olmadığını bilmeye ihtiyacın var. Genellikle, bir uygulaması equal yöntem yalnızca iki örneğin eşit olup olmadığını söyler ve neden örneklerin eşit olmadığını umursamıyoruz. - Bhesh Gurung
Hangi özelliklerin eşit olmadığını bilmek istiyorum, böylece onları düzeltebilirim. :) - Ryan Nelson
Herşey Objectvar equals Metodu, muhtemelen geçersiz kılan bir yöntem değil. - Steve Kuo
Düşünebildiğim en iyi yol, bir sarmalayıcı sınıfını veya bir alt sınıfı kullanmak ve sonra eşitlik yöntemini geçersiz kıldıktan sonra kullanmaktır. - Thihara


Cevaplar:


mockito bir yansıma-matcher sunuyor:

Assert.assertThat(expected, new ReflectionEquals(actual, excludeFields));

35
2018-04-22 09:37



Bu sınıf pakette org.mockito.internal.matchers.apachecommons. Mockito dokümanları durumu: org.mockito.internal -> "İç sınıflar, müşteriler tarafından kullanılmamalıdır." Bunu kullanarak projenizi riske atacaksınız. Bu herhangi bir Mockito sürümünde değişebilir. Burayı oku: site.mockito.org/mockito/docs/current/overview-summary.html - luboskrnac
kullanım Mockito.refEq() yerine. - Jeremy Kao
Mockito.refEq() nesneler bir kimlik kümesine sahip olmadığında başarısız olur = ( - cavpollo
@PiotrAleksanderChmielowski, Üzgünüz, Spring + JPA + Varlıkları ile çalışırken, Varlık nesnesinin bir kimliği (veritabanı tablosunun id alanını temsil eder) olabilir, bu yüzden boş olduğunda (DB'de henüz depolanmamış yeni bir nesne), refEq Hashcode yöntemi nesneleri karşılaştıramadığından karşılaştırılamaz. - cavpollo
İyi çalışıyor, ama beklenen ve gerçek yanlış sırada. Öbür türlü olmalı. - pkawiak


Genellikle bu usecase'i org.apache.commons.lang3.builder.EqualsBuilder kullanarak uygularım

Assert.assertTrue(EqualsBuilder.reflectionEquals(expected,actual));

29
2018-03-03 23:25



Gradle: androidTestCompile 'org.apache.commons: commons-lang3: 3.5' - Roel
@Roel Daha fazla bilgi ekleyebilir misiniz? - Abhijeet Kushe
"Org.apache.commons.lang3.builder.EqualsBuilder" seçeneğini kullanmak istediğinizde bunu "bağımlılıklar" altında not defterinize eklemeniz gerekir. - Roel
Bu, tam olarak hangi alanların eşleşmediğine dair herhangi bir ipucu vermez. - Vadzim
@Vadzim Bu Assert.assertEquals (ReflectionToStringBuilder.toString (beklenen), ReflectionToStringBuilder.toString (gerçek)) almak için aşağıdaki kodu kullandım; - Abhijeet Kushe


Burada birçok doğru cevap var, ancak sürümümü de eklemek istiyorum. Bu Assertj dayanmaktadır.

import static org.assertj.core.api.Assertions.assertThat;

public class TestClass {

    public void test() {
        // do the actual test
        assertThat(actualObject)
            .isEqualToComparingFieldByFieldRecursively(expectedObject);
    }
}

13
2018-06-27 13:41





Biliyorum biraz eski ama umarım yardımcı olur.

Aynı problemle karşılaşıyorum, bu yüzden, soruşturmadan sonra, bundan daha az benzer sorular buldum ve çözümü bulduktan sonra, aynı şeyi yanıtladım, çünkü başkalarına yardım edebileceğini düşündüm.

En çok oylanan cevap (yazarın seçtiği değil) benzer soru, sizin için en uygun çözümdür.

Temel olarak, denilen kütüphaneyi kullanmaktan oluşur Unitils.

Bu kullanım:

User user1 = new User(1, "John", "Doe");
User user2 = new User(1, "John", "Doe");
assertReflectionEquals(user1, user2);

Sınıfı bile olsa geçecek User uygulamıyor equals(). Daha fazla örnek ve gerçekten harika bir assert görebilirsiniz assertLenientEquals onların içinde öğretici.


8
2017-12-17 17:06





Kullanabilirsiniz Apache commons lang ReflectionToStringBuilder

Tek tek veya daha iyisi test etmek istediğiniz nitelikleri belirtebilirsiniz, istemediğinizi hariç tutabilirsiniz:

String s = new ReflectionToStringBuilder(o, ToStringStyle.SHORT_PREFIX_STYLE)
                .setExcludeFieldNames(new String[] { "foo", "bar" }).toString()

Daha sonra iki dizeyi normal olarak karşılaştırırsınız. Yansımanın yavaş olduğu nokta için, bunun sadece test için olduğunu farz ediyorum, bu yüzden çok önemli olmamalı.


4
2017-08-28 07:33



Bu yaklaşımın yararı, umursayacağınız alanlar hariç, beklenen ve gerçek değerleri görüntüleyen görsel çıktı elde etmektir. - fquinner


Kütüphane Hamcrest 1.3 Utility Matchers eşittir yerine yansımayı kullanan özel bir eşleştiriciye sahiptir.

assertThat(obj1, reflectEquals(obj2));

3
2018-01-07 10:48



Proje belgelerine göre sığ eşittir unutmayın. - pimlottc


Eğer sizden gelenler için hamcrest kullanıyorsanız (assertThat) ve ek test kütüphanelerinde kullanmak istemiyorsanız o zaman kullanabilirsiniz. SamePropertyValuesAs.samePropertyValuesAs geçersiz kılma yöntemi olmayan öğeleri belirtmek için.

Bunun tersi, başka bir test çerçevesine girmek zorunda kalmamanız ve iddianın başarısız olması durumunda faydalı bir hata verecektir (expected: field=<value> but was field=<something else>) yerine expected: true but was false eğer böyle bir şey kullanırsan EqualsBuilder.reflectionEquals().

Dezavantajı, bunun bir sığ karşılaştırma olması ve alanların dışına çıkarılması için bir seçenek olmamasıdır (EqualsBuilder'da olduğu gibi), bu yüzden yuvalanmış nesnelerin etrafında çalışmanız gerekir (ör. Bunları kaldırın ve bunları bağımsız olarak karşılaştırın).

En iyi senaryo:

import static org.hamcrest.beans.SamePropertyValuesAs.samePropertyValuesAs;
...
assertThat(actual, is(samePropertyValuesAs(expected)));

Çirkin durumda:

import static org.hamcrest.beans.SamePropertyValuesAs.samePropertyValuesAs;
...
SomeClass expected = buildExpected(); 
SomeClass actual = sut.doSomething();

NestedClass expectedSubObject = expected.getSubObject();
expected.setSubObject(null);

NestedClass actualSubObject = actual.getSubObject();
actual.setSubObject(null);

assertThat(actual, is(samePropertyValuesAs(expected)));
assertThat(actualSubObject, is(samePropertyValuesAs(expectedSubObject)));

Öyleyse, zehirini al. Ek çerçeve (örn., Unitils), yardımcı olmayan hata (örn. EqualsBuilder) veya sığ karşılaştırma (hamcrest).


1
2018-03-01 03:52





Alandaki alanı karşılaştır:

assertNotNull("Object 1 is null", obj1);
assertNotNull("Object 2 is null", obj2);
assertEquals("Field A differs", obj1.getFieldA(), obj2.getFieldA());
assertEquals("Field B differs", obj1.getFieldB(), obj2.getFieldB());
...
assertEquals("Objects are not equal.", obj1, obj2);

0
2017-08-27 18:23



Bu yapmak istemediğim bir şeydir, çünkü erken bir başarısızlık aşağıdaki olası hataları gizleyecektir. - Ryan Nelson
Maalesef, gönderilerinizin bir kısmını özledim ... Bir birim test ortamında neden "tam bir eşitlik resmi" önemlidir? Alanların hepsi eşittir (test geçer) veya hepsi eşit değildir (test başarısız olur). - EthanB
Diğer alanların eşit olmadığını anlamak için testi yeniden çalıştırmak zorunda kalmak istemiyorum. Eşit olmayan tüm alanları önceden bilmek istiyorum, böylece bunları bir kerede çözebilirim. - Ryan Nelson
Tek bir testte birçok alanın kullanılması, gerçek bir 'birim' testi olarak kabul edilmeyecektir. Geleneksel test güdümlü geliştirme (TDD) ile, küçük bir test yazıyorsunuz ve sonra sadece geçmesi için yeterli kod yazıyorsunuz. Her tarlada bir hakemin olması, doğru yolu yapmaktır, sadece tüm önerileri tek bir teste tabi tutmayın. Önemsediğiniz her alan iddiası için farklı bir test oluşturun. Bu, tüm alanları tek bir paketten geçirerek tüm hataları görmenizi sağlayacaktır. Bu zorsa, büyük olasılıkla kodunuzun ilk etapta modüler olmadığı ve muhtemelen daha temiz bir çözüm haline getirilebileceği anlamına gelir. - Jesse Webb
Bu kesinlikle geçerli bir öneridir ve tek bir testte birden fazla testi çözebileceğiniz alışılmış yoldur. Burada tek sorun, nesnenin bütünsel bir görünümünü isterim. Yani, nesnenin geçerli bir durumda olduğunu doğrulamak için tüm alanları aynı anda test etmek isterim. Bu geçersiz kılınmış bir equals () yöntemine sahip olduğunuzda yapmak zor değildir (tabii ki bu örnekte yok). - Ryan Nelson