Reputation: 2736
I am having a problem where some class X extends extends java.util.AbstractMap, and also overloads equals(Object)
. Calling org.testng.Assert.assertEquals(X a, X b)
resolves to assertEquals(Map<?,?>, Map<?,?>)
. Instead of calling the 'equals' method, the map entries are compared. This results in assertEquals(a, b)
passing even though a.equals(b)
is false.
This code demonstrates the problem:
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue;
import java.util.AbstractMap;
import java.util.Collections;
import java.util.Set;
import org.testng.annotations.Test;
public class AssertTest {
static class X extends AbstractMap<String, Object> {
private int i;
public X(int i) {
this.i = i;
}
@Override
public Set<Entry<String, Object>> entrySet() {
return Collections.EMPTY_SET;
}
@Override
public boolean equals(Object o) {
return o instanceof X && i == ((X)o).i;
}
@Override
public int hashCode() {
return i;
}
}
@Test
public void test() {
X one = new X(1);
X two = new X(2);
assertEquals(one, two); // passes, should fail IMO
assertTrue(one.equals(two)); // correctly fails
}
}
What is the best way to work around this problem? One possibility is to remember to not use assertEquals
to verify that instances of X match, but that is extremely error-prone.
Another possibility is to simply make a local copy of testng and rename the overloads. That creates an ongoing maintenance problem.
The only other idea I have is to create a project-specific Assert class that delegates to testng.Assert but renames the problematic overloads to 'assertCollectionEquals', 'assertMapEquals', etc.
Upvotes: 1
Views: 3304
Reputation: 328608
org.testng.Assert.assertEquals(X a, X b)
calls the assertEquals(Map, Map)
method which iterates over the entry set and checks that all entries are equals, bypassing the Map#equals
method, as you have noticed.
A simple cast would avoid calling that method and would use the X#equals
method instead:
assertEquals((Object) one, (Object) two);
You could also declare your variables as objects to achieve the same result:
Object one = new X(1);
Object two = new X(2);
assertEquals(one, two);
That does not realy solve your issue in the sense that it still is error prone.
To avoid the occasional error, there are a few workarounds I can think of (getting tired here so some might make little sense), using the fact that assertEquals
without cast calls x.entrySet()
:
X
is declared on the left of an =
@BeforeGroups
method (if you use groups) and put each test that relies on that idiom in the same group, so that they will use a mocked X where entrySet fails your tests (and the other methods work as expected), or at least logs/prints some warning. If you also need to use entrySet
in the same method that won't workTestNG#assertEquals(Map, Map)
to get the desired behaviourassertEquals(Map, Map)
method. If you have a static import of all assertEquals method you will have to reimplement the other signatures too :-(assertEquals(Map, Map)
method - same caveatUpvotes: 3