AssertEquals when list content is unordered [duplicate]
Asked Answered
S

7

9

How would you refactor the following if the products can be returned in any order?

List<Product> products = get_products("test_produc");
assertEquals(products.size(),3);
assertEquals(products.get(0).getName(), "test_product1");
assertEquals(products.get(1).getName(), "test_product2");
assertEquals(products.get(2).getName(), "test_produc3");

If it can be done elegantly using streams then I'm oopen to such suggestions. Hamcrest suggestions are also welcome.

Sicard answered 10/3, 2017 at 9:10 Comment(6)
@jpmc26 This is not the same question in my opinion.Sicard
Something like this?: assertThat(actual, containsInAnyOrder(expected.toArray()));Sultanate
@Sultanate Your missing getName()Sicard
@Sicard You asked how to assert that a result List contains a specific set of elements in any order. That is literally the same thing as asserting two lists contain the same elements ignoring order. How in the world is this question different? Better yet, explain why the answers in that question don't solve your problem. Because the answers over there are exactly the solutions that popped to mind when I read this question. Do you really think you're the first person to have this problem since the creation of JUnit? This is common in unit testing; of course there would be an existing post.Columniation
While I agree with @jpmc26, it should be noted that equality and ordering are different things, and while most would see it as 'completely broken' for two equal entities to not sort into the same position, if you have two entities which cannot be ordered, but are not equal, then sorting and comparing will break. Indeed, for this reason, doing an explicit set comparison is surely the right solution, and conveys your intent better (you are comparing unordered data (i.e. a set)). This alternative makes the assumption that your data can be ordered, which could bite you in the long run.Herodias
I also think this is not an exact duplicate. Since the question mentions "streams", it becomes a bit more clear that the OP wants to compare "objects" (a Product entity with a "getName()" method) with "strings" (names of products) (i.e. not explicitly calling getName() on each object again and again was(?) an important part of the question).... Or so I thought! If that was the differentiator in this question, answer from @sruetti should have been picked as an answer and not the current one... confused...News
A
10

Note that assertEquals also works on Lists and Sets directly. This is much less typing and it will give very clear error messages.

If the return values are not allowed to contain duplicates, they should return a Set instead of a List. If you can change the function you are testing is this way you can test it as follows:

assertEquals(new HashSet<>(Arrays.asList("Item1", "Item2")), get_products());

If this is not an option you should sort both the expected and the actual results and compare those:

asssertEquals(Arrays.sort(Arrays.asList("Item1", "Item2")), Arrays.sort(get_products()));

Finally you could resort to using Hamcrest matchers (the function containsInAnyOrder is in org.hamcrest.collection.IsIterableContainingInAnyOrder):

assertThat(get_products(), containsInAnyOrder("Item1", "Item2"));
Arondell answered 10/3, 2017 at 9:26 Comment(0)
M
8

I would recommend using AssertJ -

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

// ......

List<String> products = get_products("test_produc").stream()
    .map(Product::getName)
    .collect(toList());

assertThat(products).containsExactlyInAnyOrder("Third", "Second", "First");

which additionally gives you many more fluent assertions (especially the exception handling ones) as well.

Manzanilla answered 10/3, 2017 at 9:19 Comment(0)
S
3

I'd combine the answer of Abubakkar with Hamcrest compare collections

List<String> productNames = products.stream()
                                    .map(p -> p.getName())
                                    .collect(Collectors.toList());
// assert that the actual list does not contain additional elements:
assertEquals(products.size(),3);
assertThat(productNames, containsInAnyOrder("test_product1", "test_product2", "test_produc3"));
Streamer answered 10/3, 2017 at 9:26 Comment(1)
toList not asListSicard
A
2

You can assert on a boolean condition using Stream#anyMatch(Predicate filter):

Returns whether any elements of this stream match the provided predicate. May not evaluate the predicate on all elements if not necessary for determining the result. If the stream is empty then false is returned and the predicate is not evaluated.

assertEquals(products.size(), 3);
assertTrue(products.stream().anyMatch(p -> "test_product1".equals(p.getName())));
assertTrue(products.stream().anyMatch(p -> "test_product2".equals(p.getName())));
assertTrue(products.stream().anyMatch(p -> "test_product3".equals(p.getName())));
Akkad answered 10/3, 2017 at 9:17 Comment(1)
But…why? As far as I can tell, this has no benefits over using a set. You still can't detect multiplicity, if that's important. It takes quadratic time instead of linear (or linearithmic) time. It's significantly more verbose. Etc.Compellation
O
1

You can try something like this using stream (I am assuming you are only interested in getName, and I haven't used hamcrest, so using just plain assertTrue):

List<String> productNames = products.stream()
                                    .map(p -> p.getName())
                                    .collect(Collectors.toList());

assertTrue(productNames.contains("test_product1"));
assertTrue(productNames.contains("test_product2"));
assertTrue(productNames.contains("test_product3"));
Obadiah answered 10/3, 2017 at 9:17 Comment(1)
@downvoter, can you please comment what's wrong with my answer.Obadiah
I
1

In that situations i prefer to use assertJ. Maybe it will be satisfying. Example:

    final List<String> actual = Arrays.asList("c", "b", "a");
    final List<String> expected = Arrays.asList("a", "b", "c");
    Assertions.assertThat(actual).containsOnlyElementsOf(expected);
Ischia answered 10/3, 2017 at 9:28 Comment(0)
W
1

Not knowing Hamcrest, my solution would be to sort each list (or copies of them if it’s not acceptable to modify them) and then assert that they are equal.

Strength: Also works if there are duplicate products in the list (will require the same duplicate in the other list).

You may use a stream to extract the names from the product list; some of the other answers already show how.

Whorish answered 10/3, 2017 at 9:37 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.