Issue
Sometimes sonar cloud is complaining about number of assertions in tests. I wanted and found out how I can chain assertions one to another, but theres a case where I cannot figure out how I can chain assertions.
import java.util.List;
import org.assertj.core.groups.Tuple;
import org.junit.jupiter.api.Test;
import lombok.Builder;
import lombok.Data;
import static org.assertj.core.api.Assertions.assertThat;
class FakeTest
{
@Test
void fakeTest()
{
// given
var resStatus = "FINISHED";
var tc1 = "Test case 1";
var r1 = "GOOD";
var tc2 = "Test case 2";
var r2 = "ALARMING";
var res1 = Response.builder()
.status(resStatus)
.testCaseName(tc1)
.result(r1)
.build();
var res2 = Response.builder()
.status(resStatus)
.testCaseName(tc2)
.result(r2)
.build();
var result = Result.builder()
.responses(List.of(res1, res2))
.value("PASSED")
.build();
// then
assertThat(result)
.returns("PASSED", Result::getValue);
assertThat(result.getResponses())
.extracting("status", "testCaseName", "result")
.contains(
Tuple.tuple(resStatus, tc1, r1),
Tuple.tuple(resStatus, tc1, r2));
}
}
@Data
@Builder
class Result
{
private String value;
private List<Response> responses;
}
@Data
@Builder
class Response
{
private String status;
private String testCaseName;
private String result;
private String error;
}
How can I do assertions in FakeTest#fakeTest with chaining them instead of splitting to two assertions (one for assertThat(result).returns(...) and then assertThat(result.getResponses()).extracting(...)...)
What I want to achive is:
assertThat(result)
.returns("PASSED", Result::getValue)
.extracting(Result::getResponses) // connection between two assertThat(...)
.extracting("status", "testCaseName", "result")
.contains(
Tuple.tuple(resStatus, tc1, r1),
Tuple.tuple(resStatus, tc1, r2));
I've found out that returning Assertion object are different:
- ListAssert<Response> when doing the chain of second assertion
- AbstractObjectAssert<capture of ?, List<Response>> when doing merged assertion
Solution
extracting(singleParam or lambda)
can't return assertions specific to the extracted value type due to java generics limitations, in this case one can inform AssertJ of the type by using asInstanceOf which would look like:
static import org.assertj.core.api.InstanceOfAssertFactories.LIST;
assertThat(result)
.returns("PASSED", Result::getValue)
.extracting(Result::getResponses).asInstanceOf(LIST) // static import for readability
.extracting("status", "testCaseName", "result")
.contains(
Tuple.tuple(resStatus, tc1, r1),
Tuple.tuple(resStatus, tc1, r2));
InstanceOfAssertFactories provides types AssertJ supports assertions for.
Another option would be to pass a lambda that extracts the responses and their fields directly (but that's probably of bit too much work).
Answered By - Joel Costigliola
Answer Checked By - Willingham (JavaFixing Volunteer)