Eşittir yöntemi olmadan iki sınıfta eşitliği nasıl iddia ederim?


114

Diyelim ki, kaynağa sahip olmayan equals () yöntemi olmayan bir sınıfım var. Bu sınıfın iki örneğinde eşitlik olduğunu iddia etmek istiyorum.

Birden çok iddia 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 elde edemiyorum.

Manuel olarak kendi başıma karşılaştırabilir 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 bir eşitlik resmi veriyor, ancak hantal (ve olası boş problemleri bile hesaba katmadım). Üçüncü bir seçenek, Comparator'ı kullanmaktır, ancak CompareTo () hangi alanların eşitlikte başarısız olduğunu bana söylemeyecek.

Nesneden istediğimi alt sınıflara ayırmadan ve eşittir (ugh) geçersiz kılmadan elde etmek için daha iyi bir uygulama var mı?


Sizin için derinlemesine karşılaştırma yapan bir kitaplık mı arıyorsunuz? stackoverflow.com/questions/1449001/… adresinde önerilen deep-equals gibi ?
Vikdor

3
Neden iki durumun eşit olmadığını bilmeniz gerekiyor? Genellikle, bir equalyöntemin uygulanması yalnızca iki durumun eşit olup olmadığını söyler ve biz de tutarsızlıkların neden eşit olmadığını umursamıyoruz.
Bhesh Gurung

3
Hangi özelliklerin eşitsiz olduğunu bilmek istiyorum, böylece onları düzeltebilirim. :)
Ryan Nelson

Tüm Objectler bir var equalsbir yöntem, muhtemelen hiçbir geçersiz kılınan geliyordu yöntemi eşittir.
Steve Kuo

Düşünebileceğim en iyi yol, bir sarmalayıcı sınıf veya bir alt sınıf kullanmak ve sonra onu eşittir yöntemini geçersiz
kıldıktan

Yanıtlar:


67

Mockito bir yansıma eşleştirici sunar:

Mockito'nun en son sürümü için:

Assert.assertTrue(new ReflectionEquals(expected, excludeFields).matches(actual));

Daha eski sürümler için şunları kullanın:

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

18
Bu sınıf pakette org.mockito.internal.matchers.apachecommons. Mockito belgelerinin durumu: org.mockito.internal-> "Dahili sınıflar, istemciler tarafından kullanılmamalıdır." Bunu kullanarak projenizi riske atacaksınız. Bu, herhangi bir Mockito sürümünde değişebilir. Burada okuyun: site.mockito.org/mockito/docs/current/overview-summary.html
luboskrnac

7
Mockito.refEq()Bunun yerine kullanın .
Jeremy Kao

1
Mockito.refEq()nesnelerin kimliği ayarlanmamışsa başarısız olur = (
cavpollo

1
@PiotrAleksanderChmielowski, üzgünüz, Spring + JPA + Varlıklar ile çalışırken, Varlık nesnesinin bir kimliği olabilir (veritabanı tablosunun kimlik alanını temsil eder), bu nedenle boş olduğunda (henüz DB'de depolanmamış yeni bir nesne) refEqbaşarısız olur hashcode yöntemi nesneleri karşılaştıramadığından karşılaştırmak için.
cavpollo

3
İyi çalışıyor, ancak beklenen ve gerçek yanlış sırada. Öbür türlü olmalı.
pkawiak

50

Burada pek çok doğru cevap var ama ben de kendi versiyonumu eklemek istiyorum. Bu, Assertj'e dayanmaktadır.

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

public class TestClass {

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

GÜNCELLEME: assertj v3.13.2'de bu yöntem, Woodz tarafından yorumda belirtildiği gibi kullanımdan kaldırılmıştır. Mevcut öneri

public class TestClass {

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

}

3
Assertj v3.13.2'de bu yöntem kullanımdan kaldırılmıştır ve öneri şimdi ile usingRecursiveComparison()birlikte kullanılacaktır isEqualTo(), öyle ki satırassertThat(actualObject).usingRecursiveComparison().isEqualTo(expectedObject);
Woodz

46

Bu usecase'i genellikle org.apache.commons.lang3.builder.EqualsBuilder kullanarak uygularım

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

1
Gradle: androidTestCompile 'org.apache.commons: commons-lang3: 3.5'
Roel

1
"Org.apache.commons.lang3.builder.EqualsBuilder" 'ı kullanmak istediğinizde bunu gradle dosyanıza "bağımlılıklar" altına eklemeniz gerekir
Roel

3
Bu, tam olarak hangi alanların eşleşmediğine dair herhangi bir ipucu vermez.
Vadzim

1
@Vadzim Assert.assertEquals'ı (ReflectionToStringBuilder.toString (beklenen), ReflectionToStringBuilder.toString (gerçek)) almak için aşağıdaki kodu kullandım;
Abhijeet Kushe

2
Bu, grafikteki tüm düğümlerin temelde bu yöntemi neredeyse işe yaramaz hale getiren "eşit" ve "karma kod" uygulamasını gerektirir. AssertJ's isEqualToComparingFieldByFieldRecursively benim durumumda mükemmel çalışan bir programdır.
John Zhang

12

Biraz eski olduğunu biliyorum ama umarım yardımcı olur.

Seninle aynı problemle karşılaşıyorum, bu yüzden, araştırmadan sonra, bundan çok az benzer soru buldum ve çözümü bulduktan sonra, başkalarına yardım edebileceğini düşündüğüm için bunlarda da aynı cevabı veriyorum.

Bu benzer sorunun en çok oylanan yanıtı (yazar tarafından seçilen değil) sizin için en uygun çözümdür.

Temel olarak Unitils adlı kitaplığı kullanmaktan ibarettir. .

Kullanım şudur:

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

Sınıf Useruygulanmasa bile geçecek equals(). Daha fazla örnek ve eğitimlerinde adı assertLenientEqualsgeçen gerçekten harika bir iddia görebilirsiniz .


1
Maalesef Unitilsölmüş gibi görünüyor, bkz. Stackoverflow.com/questions/34658067/is-unitils-project-alive .
Ken Williams

8

Kullanabilirsiniz Apache commons lang ReflectionToStringBuilder

Tek tek test etmek istediğiniz öznitelikleri belirtebilir veya daha iyisi istemediklerinizi hariç tutabilirsiniz:

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

Daha sonra iki dizgiyi normal olarak karşılaştırırsınız. Düşünmenin yavaş olduğu noktasında, bunun sadece test için olduğunu varsayıyorum, bu yüzden çok önemli olmamalı.


1
Bu yaklaşımın ek faydası, umursamadığınız alanları hariç tutan dizeler olarak beklenen ve gerçek değerleri görüntüleyen görsel çıktı elde etmenizdir.
fquinner

7

İddialarınız (assertThat) için hamcrest kullanıyorsanız ve ek test kütüphaneleri eklemek istemiyorsanız, o zaman kullanabilirsiniz SamePropertyValuesAs.samePropertyValuesAs geçersiz kılınmış eşittir yöntemine sahip olmayan öğeleri ileri sürmek .

Bunun tersi, başka bir test çerçevesini kullanmak zorunda olmamanız ve gibi bir şey kullanmanız expected: field=<value> but was field=<something else>yerine assert ( ) başarısız olduğunda yararlı bir hata verecektir .expected: true but was falseEqualsBuilder.reflectionEquals()

Olumsuz tarafı, yüzeysel bir karşılaştırma olması ve alanları hariç tutma seçeneğinin olmamasıdır (EqualsBuilder'da olduğu gibi), bu nedenle iç içe geçmiş nesneler etrafında çalışmanız gerekir (örneğin, onları kaldırıp 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 Vaka:

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

assertThat(actual.getSubObject(), is(samePropertyValuesAs(expected.getSubObject())));    
expected.setSubObject(null);
actual.setSubObject(null);

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

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


1
Çalışmak için SamePropertyValuesAs you must add in project dependecy hamcrest.org/JavaHamcrest/…
bigspawn

Bu çözümü beğendim, çünkü "tamamen * farklı bir ek bağımlılık!
GhostCat


2

Yansıma karşılaştırma yöntemlerinden bazıları sığdır

Diğer bir seçenek, nesneyi bir json'a dönüştürmek ve dizeleri karşılaştırmaktır.

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;    
public static String getJsonString(Object obj) {
 try {
    ObjectMapper objectMapper = new ObjectMapper();
    return bjectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(obj);
     } catch (JsonProcessingException e) {
        LOGGER.error("Error parsing log entry", e);
        return null;
    }
}
...
assertEquals(getJsonString(MyexpectedObject), getJsonString(MyActualObject))


1

Alan bazında karşılaştırın:

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);

1
Bu, yapmak istemediğim bir şey, çünkü erken iddia edilen bir başarısızlık, aşağıda olası hataları gizleyecektir.
Ryan Nelson

2
Üzgünüm, gönderinizin o kısmını kaçırdım ... Birim testi ortamında "tam eşitlik resmi" neden önemlidir? Alanların tümü eşittir (test başarılı) veya hepsi eşit değildir (test başarısız olur).
EthanB

Diğer alanların eşit olup olmadığını anlamak için testi yeniden çalıştırmak istemiyorum. Eşit olmayan tüm alanları önceden bilmek istiyorum, böylece onları hemen ele alabilirim.
Ryan Nelson

1
Tek bir testte birçok alan olduğunu iddia etmek gerçek bir 'birim' testi olarak kabul edilmez. Geleneksel test güdümlü geliştirme (TDD) ile, küçük bir test yazarsınız ve ardından yalnızca geçmesi için yeterli kod yazarsınız. Alan başına bir iddiaya sahip olmak, bunu yapmanın doğru yoludur, sadece tüm iddiaları tek bir teste koymayın. Önem verdiğiniz her alan iddiası için farklı bir test oluşturun. Bu, paketin tek bir çalıştırılmasıyla tüm alanlardaki tüm hataları görmenize olanak tanır. Bu zorsa, muhtemelen kodunuzun ilk etapta yeterince modüler olmadığı ve muhtemelen daha temiz bir çözüme dönüştürülebileceği anlamına gelir.
Jesse Webb

Bu kesinlikle geçerli bir öneri ve tek bir testte birden çok iddiayı çözmenin olağan yoludur. Buradaki tek zorluk, nesnenin bütünsel bir görünümünü benimsemek. Yani, nesnenin geçerli bir durumda olduğunu doğrulamak için tüm alanları aynı anda test etmek istiyorum. Geçersiz kılınmış bir equals () yönteminiz olduğunda bunu yapmak zor değildir (tabii ki bu örnekte bende yoktur).
Ryan Nelson

1

AssertJ iddiaları, #equalsmetot uygun şekilde geçersiz kılınmadan değerleri karşılaştırmak için kullanılabilir , örneğin:

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

// ...

assertThat(actual)
    .usingRecursiveComparison()
    .isEqualTo(expected);

1

Bu soru eski olduğu için JUnit 5 kullanarak başka bir modern yaklaşım önereceğim.

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

JUnit 5 ile, Assertions.assertAll()testinizdeki tüm iddiaları bir arada gruplandırmanıza izin verecek ve her birini çalıştıracak ve sonunda başarısız olan tüm iddiaları çıkaracak bir yöntem vardır . Bu, ilk önce başarısız olan herhangi bir iddianın sonraki iddiaların yürütülmesini durdurmayacağı anlamına gelir.

assertAll("Test obj1 with obj2 equality",
    () -> assertEquals(obj1.getFieldA(), obj2.getFieldA()),
    () -> assertEquals(obj1.getFieldB(), obj2.getFieldB()),
    () -> assertEquals(obj1.getFieldC(), obj2.getFieldC()));

0

Tam eşitlik testini "otomatikleştirmek" için yansımayı kullanabilirsiniz. Tek bir alan için yazdığınız eşitlik "izleme" kodunu uygulayabilir, ardından bu testi nesnedeki tüm alanlarda çalıştırmak için yansımayı kullanabilirsiniz.


0

Bu, aynı sınıftaki iki nesneyi kendi alanlarının değerleri için karşılaştıran genel bir karşılaştırma yöntemidir (bunlara get yöntemi ile erişilebilir olduğunu unutmayın)

public static <T> void compare(T a, T b) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
    AssertionError error = null;
    Class A = a.getClass();
    Class B = a.getClass();
    for (Method mA : A.getDeclaredMethods()) {
        if (mA.getName().startsWith("get")) {
            Method mB = B.getMethod(mA.getName(),null );
            try {
                Assert.assertEquals("Not Matched = ",mA.invoke(a),mB.invoke(b));
            }catch (AssertionError e){
                if(error==null){
                    error = new AssertionError(e);
                }
                else {
                    error.addSuppressed(e);
                }
            }
        }
    }
    if(error!=null){
        throw error ;
    }
}

0

Ben de çok benzer bir vakaya rastladım.

Ben bir nesne başka aynı özellik değerlerine sahip olduğu bir sınavda karşılaştırmak istedim, ama gibi yöntemler is(), refEq()vb onun içinde boş değeri olan benim nesne gibi nedenlerle iş olmaz idözniteliği.

Demek bulduğum çözüm buydu (bir iş arkadaşım bulundu):

import static org.apache.commons.lang.builder.CompareToBuilder.reflectionCompare;

assertThat(reflectionCompare(expectedObject, actualObject, new String[]{"fields","to","be","excluded"}), is(0));

Elde edilen değer reflectionCompare0 ise, eşit oldukları anlamına gelir. -1 veya 1 ise, bazı niteliklerde farklılık gösterirler.



0

Bir Android uygulamasını birim test ederken tam olarak aynı muammayı yaşadım ve bulduğum en kolay çözüm, gerçek ve beklenen değer nesnelerimi jsondizelere dönüştürmek ve bunları dizeler olarak karşılaştırmak için Gson'u kullanmaktı .

String actual = new Gson().toJson( myObj.getValues() );
String expected = new Gson().toJson( new MyValues(true,1) );

assertEquals(expected, actual);

Bunun manuel olarak alan bazında karşılaştırmaya göre avantajları, tüm alanlarınızı karşılaştırmanızdır , böylece daha sonra sınıfınıza yeni bir alan ekleseniz bile, bir grup kullanıyor olmanıza kıyasla, otomatik olarak test edilecektir.assertEquals() . sınıfınıza daha fazla alan eklerseniz güncellenmesi gereken tüm alanlar.

jUnit ayrıca dizeleri sizin için görüntüler, böylece nerede farklı olduklarını doğrudan görebilirsiniz. GsonYine de saha siparişinin ne kadar güvenilir olduğundan emin değilim , bu potansiyel bir sorun olabilir.


1
Alan sırası Gson tarafından garanti edilmez. Dizeleri JsonParse ve ayrıştırmadan kaynaklanan JsonElement'leri karşılaştırmak isteyebilirsiniz
ozma

0

Tüm cevapları denedim ve hiçbir şey benim için gerçekten işe yaramadı.

Bu yüzden basit java nesnelerini iç içe geçmiş yapıların derinliklerine inmeden karşılaştıran kendi yöntemimi yarattım ...

Yöntem, tüm alanlar eşleşirse veya uyuşmayan ayrıntılar içeren dizeler varsa null döndürür.

Yalnızca alıcı yöntemi olan özellikler karşılaştırılır.

Nasıl kullanılır

        assertNull(TestUtils.diff(obj1,obj2,ignore_field1, ignore_field2));

Uyumsuzluk varsa örnek çıktı

Çıktı, karşılaştırılan nesnelerin özellik adlarını ve ilgili değerlerini gösterir

alert_id(1:2), city(Moscow:London)

Kod (Java 8 ve üzeri):

 public static String diff(Object x1, Object x2, String ... ignored) throws Exception{
        final StringBuilder response = new StringBuilder();
        for (Method m:Arrays.stream(x1.getClass().getMethods()).filter(m->m.getName().startsWith("get")
        && m.getParameterCount()==0).collect(toList())){

            final String field = m.getName().substring(3).toLowerCase();
            if (Arrays.stream(ignored).map(x->x.toLowerCase()).noneMatch(ignoredField->ignoredField.equals(field))){
                Object v1 = m.invoke(x1);
                Object v2 = m.invoke(x2);
                if ( (v1!=null && !v1.equals(v2)) || (v2!=null && !v2.equals(v1))){
                    response.append(field).append("(").append(v1).append(":").append(v2).append(")").append(", ");
                }
            }
        }
        return response.length()==0?null:response.substring(0,response.length()-2);
    }

0

Yalnızca junit için bir alternatif olarak, eşittir iddiasından önce alanları null olarak ayarlayabilirsiniz:

    actual.setCreatedDate(null); // excludes date assertion
    expected.setCreatedDate(null);

    assertEquals(expected, actual);

-1

Gönderdiğiniz karşılaştırma kodunu statik bir yardımcı program yöntemine koyabilir misiniz?

public static String findDifference(Type obj1, Type obj2) {
    String difference = "";
    if (obj1.getFieldA() == null && obj2.getFieldA() != null
            || !obj1.getFieldA().equals(obj2.getFieldA())) {
        difference += "Difference at field A:" + "obj1 - "
                + obj1.getFieldA() + ", obj2 - " + obj2.getFieldA();
    }
    if (obj1.getFieldB() == null && obj2.getFieldB() != null
            || !obj1.getFieldB().equals(obj2.getFieldB())) {
        difference += "Difference at field B:" + "obj1 - "
                + obj1.getFieldB() + ", obj2 - " + obj2.getFieldB();
        // (...)
    }
    return difference;
}

Bu yöntemi JUnit'te şu şekilde kullanabilirsiniz:

assertEquals ("Nesneler eşit değildir", "", findDifferences (obj1, obj));

Bu hantal değildir ve eğer varlarsa, farklılıklar hakkında size tam bilgi verir (tam olarak normal bir assertEqual biçiminde olmasa da tüm bilgileri alırsınız, böylece iyi olmalıdır).


-1

Bu, OP'ye yardımcı olmayacak, ancak burada sona eren herhangi bir C # geliştiricisine yardımcı olabilir ...

Gibi Enrique yayınlanmıştır , sen eşittir yöntemini geçersiz kılar.

Nesneden istediğimi alt sınıflara ayırmadan ve eşittir (ugh) geçersiz kılmadan elde etmek için daha iyi bir uygulama var mı?

Önerim, bir alt sınıf kullanmamaktır. Kısmi bir sınıf kullanın.

Kısmi Sınıf Tanımları (MSDN)

Yani sınıfınız şuna benzeyecektir ...

public partial class TheClass
{
    public override bool Equals(Object obj)
    {
        // your implementation here
    }
}

Java için, yansımayı kullanma önerisine katılıyorum. Mümkün olduğunca yansımayı kullanmaktan kaçınmanız gerektiğini unutmayın. Yavaştır, hata ayıklaması zordur ve gelecekte bakımı daha da zordur, çünkü IDE'ler bir alanı yeniden adlandırarak veya buna benzer bir şey yaparak kodunuzu bozabilir. Dikkatli ol!


-1

Yorumlarınızdan diğer cevaplara, ne istediğinizi anlamıyorum.

Sırf tartışma adına, sınıfın eşittir yöntemini geçersiz kıldığını varsayalım.

Böylece UT'niz şuna benzer:

SomeType expected = // bla
SomeType actual = // bli

Assert.assertEquals(expected, actual). 

Ve bitirdiniz. Dahası, iddia başarısız olursa "tam eşitlik resmi" ni elde edemezsiniz.

Anladığım kadarıyla, tür eşittir geçersiz kılmış olsa bile, "tam eşitlik resmini" elde etmek istediğiniz için onunla ilgilenmeyeceğinizi söylüyorsunuz. Yani eşitleri genişletmenin ve geçersiz kılmanın da bir anlamı yok.

Yani seçeneklere sahip olmalısınız: ya mülkü mülke göre, yansıtma kullanarak ya da sabit kodlanmış kontroller kullanarak karşılaştırırsanız, ikincisini öneririm. Veya: bu nesnelerin insan tarafından okunabilir temsillerini karşılaştırın .

Örneğin, karşılaştırmak istediğiniz türü bir XML belgesiyle serileştiren ve daha sonra ortaya çıkan XML'i karşılaştıran bir yardımcı sınıf oluşturabilirsiniz! bu durumda tam olarak neyin eşit olup neyin olmadığını görsel olarak görebilirsiniz.

Bu yaklaşım size resmin tamamına bakma fırsatı verecektir, ancak aynı zamanda nispeten külfetli (ve ilk başta biraz hataya meyillidir).


Benim "tam eşitlik resmi" terimim kafa karıştırıcı olabilir. Eşittir () işlevinin uygulanması gerçekten de sorunu çözecektir. Testi yeniden çalıştırmak zorunda kalmadan tüm eşitsiz alanları (eşitlikle ilgili) aynı anda bilmekle ilgileniyorum. Nesneyi dizgeleştirmek de başka bir olasılık, ancak derin bir eşitliğe ihtiyacım yok. Mümkünse özelliklerin equals () uygulamalarını kullanmak istiyorum.
Ryan Nelson

Harika! Mutlaka sorunuzda belirttiğiniz mülklerin eşitlerinden yararlanabilirsiniz. Görünüşe göre bu, bu durumda en basit çözüm, ancak belirttiğiniz gibi, kod çok çirkin olabilir.
Vitaliy

-3

Sınıfın eşittir yöntemini şu şekilde geçersiz kılabilirsiniz:

@Override
public int hashCode() {
    int hash = 0;
    hash += (app != null ? app.hashCode() : 0);
    return hash;
}

@Override
public boolean equals(Object object) {
    HubRule other = (HubRule) object;

    if (this.app.equals(other.app)) {
        boolean operatorHubList = false;

        if (other.operator != null ? this.operator != null ? this.operator
                .equals(other.operator) : false : true) {
            operatorHubList = true;
        }

        if (operatorHubList) {
            return true;
        } else {
            return false;
        }
    } else {
        return false;
    }
}

Bir sınıftan iki nesneyi karşılaştırmak istiyorsanız, bir şekilde eşittir ve karma kod yöntemini uygulamalısınız.


3
ama OP eşitleri geçersiz kılmak istemediğini söylüyor, daha iyi bir yol istiyor
Ankur
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.