There is a recent breaking change to AssertJ which breaks JsonContentAssert.extractingJsonPath*()
functions in Spring Boot Test.
For example JsonContentAssert.extractingJsonPathStringValue(CharSequence expression, Object... args)
expects to return AbstractCharSequenceAssert<?, String>
, but now Assertions.assertThat(String actual)
returns StringAssert
instead which causes NoSuchMethodError
to be thrown.
This problem can be replicated with Spring Boot 2.5.1 and AssertJ 3.20.0 with following example:
Code example
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.json.JsonTest;
import org.springframework.boot.test.json.JacksonTester;
import org.springframework.boot.test.json.JsonContent;
import java.io.IOException;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
class SomeClass {
private final String field;
SomeClass(String field) {
this.field = field;
}
public String getField() {
return field;
}
}
@JsonTest
public class BrokenSerializationTest {
@Autowired
private JacksonTester<SomeClass> jacksonTester;
@Test
public void valueFromJsonPathCanNotBeExtracted() throws IOException {
JsonContent<SomeClass> serialized = jacksonTester.write(new SomeClass("I am a string"));
assertThatThrownBy(() -> assertThat(serialized).extractingJsonPathStringValue("field"))
.isInstanceOf(NoSuchMethodError.class)
.hasMessage("'org.assertj.core.api.AbstractStringAssert org.assertj.core.api.Assertions.assertThat(java.lang.String)'");
}
}
Comment From: wilkinsona
Thanks for letting us know. While the change is binary incompatible it appears to be source compatible so this won't be a problem in Spring Boot 2.6 where we'll upgrade to AssertJ 3.20.x (or later). We could fix the problem in earlier versions by using reflection to call assertThat
so that the return type isn't baked into Boot's byte code. Flagging for team attention so we can decide if that extra complexity is worth it.
Comment From: snicoll
I personally don't think this is worth it. IMO, this is one of those cases where an upgrade of Spring Boot is needed to support a newer version of a third party library.
@Urokhtor can you share why you've overridden the assertJ version? (We support 3.19.0).
Comment From: wilkinsona
This branch contains the changes that are necessary to support 3.20 at runtime having compiled against an earlier version.
Comment From: philwebb
The reflection hack doesn't seem too bad to me. Perhaps we can only do it as a fallback when NoSuchMethodError
is thrown?
Comment From: Urokhtor
I personally don't think this is worth it. IMO, this is one of those cases where an upgrade of Spring Boot is needed to support a newer version of a third party library.
@Urokhtor can you share why you've overridden the assertJ version? (We support 3.19.0).
We don't use AssertJ only through Spring Boot, so we've defined it explicitly as a dependency. We noticed this problem because we automatically update our dependencies and we noticed couple of tests failing with most recent version of AssertJ.
Comment From: wilkinsona
I've added a second commit to my branch that only uses reflection as a fallback. It's a little more verbose as you'd expect, but it does feel less risky.
Comment From: snicoll
There's a 3.20.2 that reverts some binary incompatible change in the meantime. I wonder if that would fix this issue.
Comment From: wilkinsona
There's still a slight binary incompatibility with 3.20.2 but it only affects our tests. As such, I don't think we need to do anything any more.
@Urokhtor If you'd like to use AssertJ 3.20.x with Spring Boot 2.5.x (or earlier) please use AssertJ 3.20.2.