New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add WebTestClient
#4339
Comments
Could also integrate with AssertJ and Kotlin/Scala assertions. |
In addition to assertion, it would be useful to support mocking for isolated unit tests. Related work: val testingBackend = SttpBackendStub.synchronous
.whenRequestMatches(_.uri.path.startsWith(List("a", "b")))
.thenRespond("Hello there!")
.whenRequestMatches(_.method == Method.POST)
.thenRespondServerError()
val response1 = basicRequest.get(uri"http://example.org/a/b/c").send(testingBackend)
// response1.body will be Right("Hello there") |
Hi @ikhoon 😄 May I work on this issue? |
For sure, go ahead. |
@ikhoon I have a question about implementation direction. For example, if the result type of The first approach is a implementation that does not depend on AssertJ: class TestHttpResponse {
AssertStatus assertStatus() {
return new AssertStatus(this.status, status())
}
}
class AssertStatus {
HttpStatus actual;
TestHttpResponse back;
…constructor
TestHttpResponse isEqualTo(HttpStatus expected) {
checkState(actual.equals(expected), "Some message");
return back;
}
}
@Test
void test() {
assertThatCode(() -> {
client.get(…).execute()
.assertStatus().isEqualTo(HttpStatus.OK);
}).doesNotThrowAnyException();
} The first approach requires creating appropriate intermediate classes, such as The second approach is an implementation that depends on AssertJ. class TestHttpResponse {
AssertEntity<AbstractComparableAssert<?, HttpStatus>> assertStatus(HttpStatus status) {
return new AssertEntity<AbstractComparableAssert<?, HttpStatus>>(assertThat(status), this);
}
}
class AssertEntity<T> {
T entity;
TestHttpResponse back;
…constructor
TestHttpResponse that(Consumer<T> consumer) {
consumer.accept(entity);
return back;
}
}
@Test
void test() {
client.get(…).execute()
.assertStatus().that(a -> a.isEqualTo(HttpStatus.OK))
} The second approach has a simpler implementation compared to the first approach, as it only requires one intermediate class (AssertEntity) since it uses AssertJ directly. However, this approach is limited to using only the assertion methods provided by AssertJ and cannot utilize any domain-specific assertion methods. Would you please share your thoughts on which approach you believe is better, or if neither of them is the expected approach? |
If a test client depends on AssertJ, I propose to design the code like: public final class AssertableHttpResponse {
private final AggregatedHttpResposne response;
public AbstractComparableAssert<...> assertStatus() {
return asserThat(resposne.status());
}
} We can use this assertion by wrapping a test client with @RegisterExtension
ServerExtension server = new ServerExtension(...) { ... }
@Test
void shouldReturn200OK() {
// Need a better name. 🤔
AssertableWebClient client = AssertableWebClient.of(server.webClient());
client.get(...)
.execute()
.assertStatus().isEqualTo(HttpStatus.OK)
.assertHeaders().contains(entry(HttpHeaderNames.XXX, val))
.assertHeaders(HttpHeaderNames.YYY).isEqualTo("...")
.assertContent().isEqualTo("Resposne body");
} As
It is also a good approach because a test client is can be created from |
In addition to fluent assertions, we may provide a way to set mock responses to the WebTestClient client = WebTestClient.mock();
client.expect("/foo", HttpResponse.of(OK));
assert client.get("/foo").status() == HttpStatus.OK;
assert client.get("/bar").status() == HttpStatus.NOT_FOUND; This API will be useful when the server is unavailable in test env or difficult to make idempotent responses. |
It should be helpful to assert a response to a test request fluently.
The text was updated successfully, but these errors were encountered: