Search code examples
javajava-8junit5spring-testassertj

Is the method "hasOnlyElementsOfType" deprecated in Assertj 3.16.1


I've this code that no longer works after updating to Assertj 3.16.1

Throwable thrown = catchThrowable(() -> myObject.methodThrowsException());
assertThat(thrown).isInstanceOf(MyCustomException.class).extracting("fault").hasOnlyElementsOfType(CustomException.class).extracting("code").containsExactly("AnotherCustomException");

I get this error message:

java:cannot find symbol
symbol: method hasOnlyElementsOfType(java.lang.Class<CustomException.class>)
location: class org.assertj.core.api.AbstractObjectAssert<capture#1 of?, capture#2 of ?>

It's either it's deprecated or implemented differently now. I've gone through the documentation and searched this for almost two days now to see if there's any information about how to use it as opposed to how it was used previously to enable easy transition but not found anything yet. I actually get a similar error when I use this containsOnlyOnceElementsOf in place of the one giving the issue. Are there any alternatives to these methods that achieve the same thing?

Any help will be appreciated!!!


Solution

  • It looks like you upgraded from AssertJ 3.12 or earlier.

    The error is that the AbstractObjectAssert class doesn't have a hasOnlyElementsOfType method. Moreover, it has never had that method, so it's not the case that the method was deprecated and removed. Instead, this code must have worked because hasOnlyElementsOfType was being called on some other class.

    Most things in AssertJ seem to go through AbstractObjectAssert. Looking at AbstractObjectAssert in AssertJ 3.12, I see that it has a method extracting(String...) -- a varargs method -- that returns AbstractListAssert. This class in turn has a hasOnlyElementsOfType method inherited from AbstractIterableAssert. The code extracting("fault") ends up being a varargs call. This in turn returns AbstractListAssert and thus the subsequent call to hasOnlyElementsOfType works.

    However, in AssertJ 3.13, AbstractObjectAssert has a new method extracting(String) -- NOT a varargs call. That method returns AbstractObjectAssert which as we've seen doesn't have the hasOnlyElementsOfType method. When compiled against this version of AssertJ (or later), the code extracting("fault") resolves to the one-arg overload. This returns AbstractObjectAssert which does NOT have the hasOnlyElementsOfType method, hence the error.

    To work around this, you could force the call to extracting to call the varargs overload instead of the one-arg overload. A varargs call is just some syntax for passing an array in that position, so you could change your code to something like this:

    ....extracting(new String[] { "fault" })....
    

    This ends up calling the varargs overload, which returns AbstractListAssert, which has the hasOnlyElementsOfType method that you want to call next, so things should work even in recent AssertJ versions.

    As an aside, this is an example of a fairly rare case where adding a method results in an incompatibility. Usually adding methods doesn't affect any existing code. However, adding a new overload to an existing API (as AssertJ did in 3.13) potentially affects overload resolution of existing source code. That is, existing source code compiled against the old version will end up calling some method. When the same source code is compiled against the new version, it might end up calling a different method in the new API. If that new method has different behavior, it could result in subtle bugs. In this case, the new method has a different return type, so code that expected the old return type no longer works. That's exactly what happened here.