TestClient
TestClient is the workhorse used by serverTest and can be constructed manually when you need finer control.
import 'package:shelf/shelf.dart' as shelf;
import 'package:server_testing/server_testing.dart';
import 'package:server_testing_shelf/server_testing_shelf.dart';
final app = (shelf.Request req) async => shelf.Response.ok('ok');
final handler = ShelfRequestHandler(app);
final client = TestClient(handler); // defaults to in-memory
final serverClient = TestClient.ephemeralServer(handler);
final res = await client.get('/');
res.assertStatus(200).assertBody('ok');
await client.close(); // always close manually when not using serverTest
await serverClient.close(); // server transport also frees sockets
Construction Options
TestClient(handler)orTestClient.inMemory(handler)– in-memory mode.TestClient(handler, mode: TransportMode.ephemeralServer)orTestClient.ephemeralServer(handler)– realHttpServer.- Pass a
TransportOptionsobject to tweak defaults (e.g. customremoteAddress). - Retrieve the actual base URL in server mode via
await client.baseUrlFuture.
The client keeps a reference to the RequestHandler. When the client is closed it calls handler.close() so your adapter can dispose external resources.
Request Helpers
get,post,put,patch,delete,head,options- JSON variants:
getJson,postJson,putJson,patchJson,deleteJson - Multipart builder:
multipart('/upload', (builder) { ... }) - Low-level custom payload: pass a
String, bytes,Map, orListas thebodyargument requestfor custom method/URL combinations- Headers: supply
Map<String, List<String>>via the optionalheadersparameter - Cookies: populate the
cookieheader manually or rely on server-generatedSet-Cookieheaders and capture from theTestResponse
final create = await client.postJson('/users', {'name': 'Ada'});
create.assertStatus(201);
final update = await client.put('/users/1', 'name=Grace', headers: {
'content-type': ['application/x-www-form-urlencoded'],
});
update.assertStatus(200);
Multipart Builder
client.multipart accepts a callback that receives a MultipartRequestBuilder:
final res = await client.multipart('/upload', (builder) {
builder.addField('name', 'fixture');
builder.addFileFromBytes(
name: 'avatar',
filename: 'avatar.png',
bytes: pngBytes,
contentType: MediaType('image', 'png'),
);
});
The helpers take care of building the boundary, headers, and encoding.
Response Lifecycle
final response = await client.get('/users');
response
.assertStatus(200)
.assertJson((json) => json.count('users', 1));
See TestResponse for the full set of assertion helpers and JSON utilities.
Tips
- Wrap the client creation inside
serverTestunless you have a custom lifecycle to manage. - Always close manually when you instantiate the client yourself (
await client.close()). - Reuse a single handler across multiple tests only when it is stateless; otherwise prefer building a fresh handler inside each
serverTest. - Use
baseUrlFutureto integrate with browser tests or external HTTP clients that expect a real URL.