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.

Baz
  • 12,713
  • 38
  • 145
  • 268
  • 1
    @jpmc26 This is not the same question in my opinion. – Baz Mar 10 '17 at 09:16
  • Something like this?: assertThat(actual, containsInAnyOrder(expected.toArray())); – Morvader Mar 10 '17 at 09:19
  • @Morvader Your missing getName() – Baz Mar 10 '17 at 09:22
  • 3
    @Baz 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. – jpmc26 Mar 10 '17 at 09:30
  • 1
    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. – VisualMelon Mar 10 '17 at 12:07
  • 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... – OzgurH Nov 13 '17 at 18:06

7 Answers7

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"));
Thirler
  • 20,239
  • 14
  • 63
  • 92
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.

MD Sayem Ahmed
  • 28,628
  • 27
  • 111
  • 178
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"));
Community
  • 1
  • 1
sruetti
  • 532
  • 2
  • 7
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())));
M A
  • 71,713
  • 13
  • 134
  • 174
  • 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. – wchargin Mar 10 '17 at 13:17
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"));
Abubakkar
  • 15,488
  • 8
  • 55
  • 83
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);
K. Gol
  • 1,391
  • 12
  • 15
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.

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161