AssertJ `containsExactly` assertion on list with wildcard
Asked Answered
R

1

7

I have a getter returning a List with a wildcard:

import java.util.List;

public interface Foo {
    List<? extends Bar> getList();
}

Where Bar is an other interface.

When I write an assertion with AssertJ like this:

assertThat(foo.getList()).containsExactly(bar1, bar3);

EDIT: my complete usage is to chain a usingElementComparator and to provide a Comparator<Bar> to compare the expected Bar instances.

Comparator<Bar> comparator = createBarComparator()
assertThat(foo.getList()).usingElementComparator(comparator).containsExactly(bar1, bar3);

I get this compilation error:

The method containsExactly(capture#46-of ? extends Bar...) in the type ListAssert is not applicable for the arguments (Bar, Bar)


My first solution is to cast the result:

assertThat((List<Bar>)foo.getList()).containsExactly(bar1, bar3);

Then I get a warning:

Type safety: Unchecked cast from List to List

Warning can be removed with @SuppressWarnings("unchecked"), but still the cast in the middle do not make the assertion really readable.


My second solution is to indicate the value the ELEMENT generic parameter:

Assertions.<Bar>assertThat(foo.getList()).containsExactly(bar1, bar3);

A little better, but also not that nice (no static import possible, the beginning of the line do not facilitate the readability)


I guess I am looking for an other assertThat method for list, where the class type can be specified as second parameter:

@CheckReturnValue
public static <ELEMENT> ListAssert<ELEMENT> assertThat(List<? extends ELEMENT> actual, Class<ELEMENT> c) {
    return AssertionsForInterfaceTypes.assertThat(actual);
}

This way I should be able to write something like this:

Assertions.assertThat(foo.getList(), Bar.class).containsExactly(bar1, bar3);
Rift answered 15/11, 2017 at 21:18 Comment(0)
B
3

That used to work with the Oracle JDK 7 compiler but this was actually a bug in the compiler, this has been fixed in Java 8 JDK so having the compilation error is the normal behavior (can't find the bug reference though).

I would be happy to support but I'm not sure this is possible in AssertJ except by removing all generics usage in collections assertions.

assertThat(List, Class) already exists but for another purpose so no luck for that option.

A possible hack is to define your own assertThat method like that:

public static <T> ListAssert<Object> assertThat(final List<T> list) {
    return Assertions.assertThat(list);
}

The trick being to return a ListAssert<Object>.

Although I understand the rationale of the compilation error I disagree with it for read only method.

Bradley answered 16/11, 2017 at 2:14 Comment(2)
Thank you for your detailed answer. Do not remove all generics usage in collection assertions. They are 95% correct and helpfull. Returning a ListAssert<Object> is not working for me, because I also use a usingElementComparatormethod in the chain (see updated question). I might go with own assertThatListOf(Class, List) By the way, THANK YOU for AssertJ, It is a great library!Rift
Thanks for the kind words, appreciated.Bradley

© 2022 - 2024 — McMap. All rights reserved.