119

How would you succinctly assert the equality of Collection elements, specifically a Set in JUnit 4?

Neuron
  • 5,141
  • 5
  • 38
  • 59
Eqbal
  • 4,722
  • 12
  • 38
  • 47

11 Answers11

120

You can assert that the two Sets are equal to one another, which invokes the Set equals() method.

public class SimpleTest {

    private Set<String> setA;
    private Set<String> setB;

    @Before
    public void setUp() {
        setA = new HashSet<String>();
        setA.add("Testing...");
        setB = new HashSet<String>();
        setB.add("Testing...");
    }

    @Test
    public void testEqualSets() {
        assertEquals( setA, setB );
    }
}

This @Test will pass if the two Sets are the same size and contain the same elements.

Neuron
  • 5,141
  • 5
  • 38
  • 59
Bill the Lizard
  • 398,270
  • 210
  • 566
  • 880
  • 8
    This does not display very good results in the report. If your toStrings are clearly defined it is better, but still not good (A small difference can end up with a page of text) – Bill K Sep 17 '12 at 18:13
  • Uhm, how come I get: java.lang.AssertionError: expected: java.util.Hashtable<{CompanyName=8PKQ9va3nW8pRWb4SjPF2DvdQDBmlZ, Ric=sZwmXAdYKv, Category=AvrIfd, QuoteId=4342740204922826921}> but was: java.util.Hashtable<{CompanyName=8PKQ9va3nW8pRWb4SjPF2DvdQDBmlZ, Ric=sZwmXAdYKv, Category=AvrIfd, QuoteId=4342740204922826921}> – Giovanni Botta Mar 28 '13 at 15:27
  • 4
    @Giodude Do you have `equals` and `hashCode` implemented in the class that you're storing in your Hashtable? – Bill the Lizard Mar 28 '13 at 15:40
  • As you can see those are just strings and a long... I'm testing Avro to serialize and de-serialize a map and that's the result. I think there's gotta be something fishy going on with the way the strings are serialized and de-serialized that makes the test fail but I can't seem to find the problem. – Giovanni Botta Mar 28 '13 at 17:35
  • Didn't work for me even though I am comparing two HashSet. @MattFriedman answer actually works for my use case. – bluecollarcoder Nov 06 '14 at 00:28
  • I've found that if assertEquals is failing for two map comparisons with complex nesting and custom types, but you're sure that they're equal, then you should double check that all your custom types are implementing equals. If you've forgotten even one, then you'll just get the unhelpful message that you're map references are different. – RTF Jan 14 '15 at 11:41
  • I'm afraid that the tests will fail for those cases that has a HashSet from one side and a decorated hash set from the other side (for example, an immutable hash set). I would recommend to use comparison using twice the containsAll method. A = B if A contains B and B contains A. – Victor Sep 18 '15 at 18:45
  • I came upon this question because I think assuming `HashSet` is an unreasonable assumption. If the publicly stated return type is `Set`, then the class under test should have the freedom to use any implementation of `Set`. – Alonso del Arte Jul 03 '23 at 20:44
49

Apache commons to the rescue again.

assertTrue(CollectionUtils.isEqualCollection(coll1, coll2));

Works like a charm. I don't know why but I found that with collections the following assertEquals(coll1, coll2) doesn't always work. In the case where it failed for me I had two collections backed by Sets. Neither hamcrest nor junit would say the collections were equal even though I knew for sure that they were. Using CollectionUtils it works perfectly.

oleksii
  • 35,458
  • 16
  • 93
  • 163
Matt Friedman
  • 1,535
  • 1
  • 17
  • 24
  • 21
    This is actually trivial, the tricky part is to clearly indicate the difference to the caller – Bill K Sep 17 '12 at 18:15
  • 2
    The accepted answer is a good answer for the original question (unit test specifically for two Sets) but this answer with CollectionUtils is I think a better answer for the most general case. I wasn't able to compare a Collection and a Set unless using CollectionUtils. – Jason Mar 15 '17 at 09:46
16

with hamcrest:

assertThat(s1, is(s2));

with plain assert:

assertEquals(s1, s2);

NB:t the equals() method of the concrete set class is used

dfa
  • 114,442
  • 31
  • 189
  • 228
7

A particularly interesting case is when you compare

   java.util.Arrays$ArrayList<[[name,value,type], [name1,value1,type1]]> 

and

   java.util.Collections$UnmodifiableCollection<[[name,value,type], [name1,value1,type1]]>

So far, the only solution I see is to change both of them into sets

assertEquals(new HashSet<CustomAttribute>(customAttributes), new HashSet<CustomAttribute>(result.getCustomAttributes()));

Or I could compare them element by element.

  • Actually, there are several solutions for that presented in the other answers. Sets are a bit unfortunate for this, anyway, since they ignore the order. Perhaps ArrayList? – Dr. Hans-Peter Störr May 24 '17 at 15:46
4

As an additional method that is array based ... you can consider using unordered array assertions in junitx . Although the Apache CollectionUtils example will work, there is a pacakge of solid assertion extensions there as well :

I think that the

ArrayAssert.assertEquivalenceArrays(new Integer[]{1,2,3}, new Integer[]{1,3,2});

approach will be much more readable and debuggable for you (all Collections support toArray(), so it should be easy enough to use the ArrayAssert methods.

Of course the downside here is that, junitx is an additional jar file or maven entry...

 <dependency org="junit-addons" name="junit-addons" rev="1.4"/>
jayunit100
  • 17,388
  • 22
  • 92
  • 167
2

Check this article. One example from there:

@Test  
public void listEquality() {  
    List<Integer> expected = new ArrayList<Integer>();  
    expected.add(5);  

    List<Integer> actual = new ArrayList<Integer>();  
    actual.add(5);  

    assertEquals(expected, actual);  
}  
double-beep
  • 5,031
  • 17
  • 33
  • 41
Roman
  • 64,384
  • 92
  • 238
  • 332
1

Using Hamcrest:

assertThat( set1, both(everyItem(isIn(set2))).and(containsInAnyOrder(set1)));

This works also when the sets have different datatypes, and reports on the difference instead of just failing.

Dr. Hans-Peter Störr
  • 25,298
  • 30
  • 102
  • 139
1

I like the solution of Hans-Peter Störr... But I think it is not quite correct. Sadly containsInAnyOrder does not accept a Collection of objetcs to compare to. So it has to be a Collection of Matchers:

assertThat(set1, containsInAnyOrder(set2.stream().map(IsEqual::equalTo).collect(toList())))

The import are:

import static java.util.stream.Collectors.toList;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.junit.Assert.assertThat;
FLUXparticle
  • 733
  • 6
  • 16
0

You can do it like that ->
assertThat(actualSet).containsExactlyInAnyOrder(expectedSet);

Yusuf BESTAS
  • 91
  • 1
  • 6
-1

If you want to check whether a List or Set contains a set of specific values (instead of comparing it with an already existing collection), often the toString method of collections is handy:

String[] actualResult = calltestedmethod();
assertEquals("[foo, bar]", Arrays.asList(actualResult).toString());

List otherResult = callothertestedmethod();
assertEquals("[42, mice]", otherResult.toString());

This is a bit shorter than first constructing the expected collection and comparing it with the actual collection, and easier to write and correct.

(Admittedly, this is not a particularily clean method, and can't distinguish an element "foo, bar" from two elements "foo" and "bar". But in practice I think it's most important that it's easy and fast to write tests, otherwise many developers just won't without being pressed.)

Dr. Hans-Peter Störr
  • 25,298
  • 30
  • 102
  • 139
  • This makes the result of your unit test depend on the implementation of toString from list. If they decide to change the formatting the unit test wont work anymore. I would not consider this safe. – Laurens Op 't Zandt Mar 24 '17 at 11:36
  • @LaurensOp'tZandt You mean Oracle changing the format of Collection.toList()? That's certainly not going to happen. You are, however, right that's not particularily clean. But in practice, my impression that it's most important that it is very easy to write tests. – Dr. Hans-Peter Störr Mar 25 '17 at 19:13
  • I agree, I think the toString method won't likely chance. So probably it will keep working. I just wanted to point out that its not a very clean way. But indeed it is very easy. One problem that arises is when comparing sets. Since their order is not guaranteed. – Laurens Op 't Zandt Mar 26 '17 at 19:57
-1

containsInAnyOrder() - help me.

  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Jul 11 '23 at 17:40
  • This does not provide an answer to the question. Once you have sufficient [reputation](https://stackoverflow.com/help/whats-reputation) you will be able to [comment on any post](https://stackoverflow.com/help/privileges/comment); instead, [provide answers that don't require clarification from the asker](https://meta.stackexchange.com/questions/214173/why-do-i-need-50-reputation-to-comment-what-can-i-do-instead). - [From Review](/review/late-answers/34667877) – Bö macht Blau Jul 12 '23 at 19:04