How do I assert equality on two classes without an equals method?

后端 未结 23 1371
臣服心动
臣服心动 2020-11-28 05:20

Say I have a class with no equals() method, to which do not have the source. I want to assert equality on two instances of that class.

I can do multiple asserts:

相关标签:
23条回答
  • 2020-11-28 06:07

    This won't help the OP, but it might help any C# developers who end up here...

    Like Enrique posted, you should override the equals method.

    Is there a better practice to get what I want from the object, without subclassing and overridding equals (ugh)?

    My suggestion is to not use a subclass. Use a partial class.

    Partial Class Definitions (MSDN)

    So your class would look like...

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

    For Java, I would agree with the suggestion to use reflection. Just remember that you should avoid using reflection whenever possible. It is slow, hard to debug, and even harder to maintain into the future because IDEs could break your code by doing a field rename or something like that. Be careful!

    0 讨论(0)
  • 2020-11-28 06:08

    In common case with AssertJ you can create custom comparator strategy:

    assertThat(frodo).usingComparator(raceComparator).isEqualTo(sam)
    assertThat(fellowshipOfTheRing).usingElementComparator(raceComparator).contains(sauron);
    

    Using a custom comparison strategy in assertions

    AssertJ examples

    0 讨论(0)
  • 2020-11-28 06:08

    I tried all the answers and nothing really worked for me.

    So I've created my own method that compares simple java objects without going deep into nested structures...

    Method returns null if all fields match or string containing mismatch details.

    Only properties that have a getter method are compared.

    How to use

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

    Sample output if there is a mismatch

    Output shows property names and respective values of compared objects

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

    Code (Java 8 and above):

     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 讨论(0)
  • 2020-11-28 06:11

    If you're using hamcrest for your asserts (assertThat) and don't want to pull in additional test libs, then you can use SamePropertyValuesAs.samePropertyValuesAs to assert items that don't have an overridden equals method.

    The upside is that you don't have to pull in yet another test framework and it'll give a useful error when the assert fails (expected: field=<value> but was field=<something else>) instead of expected: true but was false if you use something like EqualsBuilder.reflectionEquals().

    The downside is that it is a shallow compare and there's no option for excluding fields (like there is in EqualsBuilder), so you'll have to work around nested objects (e.g. remove them and compare them independently).

    Best Case:

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

    Ugly Case:

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

    So, pick your poison. Additional framework (e.g. Unitils), unhelpful error (e.g. EqualsBuilder), or shallow compare (hamcrest).

    0 讨论(0)
  • 2020-11-28 06:11

    AssertJ assertions can be used to compare the values without #equals method properly overridden, e.g.:

    import static org.assertj.core.api.Assertions.assertThat; 
    
    // ...
    
    assertThat(actual)
        .usingRecursiveComparison()
        .isEqualTo(expected);
    
    0 讨论(0)
提交回复
热议问题