Passing Data with Promise
Promise<T> lets a step produce a value that a later step consumes. Because all steps are registered before any execute, Promise bridges the gap between step registration time and step execution time.
The problem without Promise
Steps are compiled into the state machine at registration time. You cannot call rest.get("/some/url").getBody() during registration because the request hasn't happened yet. Promise is the solution.
How Promise works
- A producing step calls
promiseResponse()/PromiseResponse(), which returns aPromise<T>. - At execution time, the framework settles the promise after the step completes.
- A consuming step wraps
promise.resolve()/promise.Resolve()inside aSupplier/Func— this lambda is not called until execution time.
Critical rule: Never call promise.resolve() / promise.Resolve() eagerly (outside a lambda). It will throw IllegalStateException / InvalidOperationException because the promise is not yet settled.
Passing a typed REST response to the next step
- .NET
- Java
await XceptoTest.Given(scenario, builder =>
{
var rest = builder.RestAdapterBuilder()
.WithBaseUrl(scenario.ApiAddress)
.Build();
Promise<TokenResponse> token = rest.Post("/auth/login")
.WithRequestBody(() => new LoginRequest(username, password))
.WithResponseType<TokenResponse>()
.PromiseResponse();
rest.Get("/api/me")
.WithBearerTokenClient(() => token.Resolve().AccessToken) // lazy!
.AssertSuccess();
});
Xcepto.given(scenario, builder -> {
var rest = new RestAdapterBuilder(builder)
.withBaseUrl(scenario.apiAddress)
.withSerializer(new GsonSerializer())
.build();
Promise<TokenResponse> token = rest.post("/auth/login")
.withRequestBody(() -> new LoginRequest(username, password))
.withResponseType(TokenResponse.class)
.promiseResponse();
rest.get("/api/me")
.withBearerTokenClient(() -> token.resolve().accessToken) // lazy!
.assertSuccess();
}, TIMEOUT, Duration.ofMillis(100));
Passing HTML body from SSR to REST
- .NET
- Java
await XceptoTest.Given(scenario, builder =>
{
var ssr = builder.SsrAdapterBuilder()
.WithBaseUrl(scenario.GuiAddress)
.Build();
var rest = builder.RestAdapterBuilder()
.WithBaseUrl(scenario.ApiAddress)
.Build();
Promise<string> pageHtml = ssr.Post("/token/create")
.WithFormContent(new TokenCreateRequest("deploy-key").ToForm())
.AssertSuccess()
.PromiseResponse(); // Promise<string> — full HTML body
rest.Post("/api/env/create")
.WithBearerTokenClient(
() => ExtractTokenFromPage(pageHtml.Resolve())) // lazy
.AssertSuccess();
});
Xcepto.given(scenario, builder -> {
var ssr = new SsrAdapterBuilder(builder)
.withBaseUrl(scenario.guiAddress)
.build();
var rest = new RestAdapterBuilder(builder)
.withBaseUrl(scenario.apiAddress)
.withSerializer(new GsonSerializer())
.build();
Promise<String> pageHtml = ssr.post("/token/create")
.withFormContent(SsrExtensions.toForm(
new TokenCreateRequest("deploy-key")))
.assertSuccess()
.promiseResponse(); // Promise<String> — full HTML body
rest.post("/api/env/create")
.withBearerTokenClient(
() -> extractTokenFromPage(pageHtml.resolve())) // lazy
.assertSuccess();
}, TIMEOUT, Duration.ofMillis(100));
Driving a dynamic path with Promise
- .NET
- Java
Promise<ResourceRef> ref = rest.Get("/api/first")
.WithResponseType<ResourceRef>()
.PromiseResponse();
rest.Get(() => ref.Resolve().Path) // lazy path from first response
.AssertSuccess();
Promise<ResourceRef> ref = rest.get("/api/first")
.withResponseType(ResourceRef.class)
.promiseResponse();
rest.get(() -> ref.resolve().path()) // lazy path supplier
.assertSuccess();
Promise types by adapter
| Adapter | promiseResponse() type | Content |
|---|---|---|
REST (no withResponseType) | Promise<string> / Promise<String> | Raw response body |
REST (with withResponseType<T>) | Promise<T> | Deserialized object |
| SSR | Promise<string> / Promise<String> | Full HTML body |