Skip to main content

Server Examples

These snippets show how to combine handler providers, transports, and fixtures when writing HTTP-focused tests.

Basic dart:io Handler

Use the built-in IoRequestHandler for simple HttpServer applications.

import 'dart:io';
import 'package:server_testing/server_testing.dart';

void main() {
Future<void> handleRequest(HttpRequest request) async {
final response = request.response;

if (request.uri.path == '/ping') {
response.statusCode = 200;
response.headers.contentType = ContentType.json;
response.write('{"message": "pong"}');
} else {
response.statusCode = 404;
response.write('Not found');
}

await response.close();
}

final handler = IoRequestHandler(handleRequest);

serverTest('GET /ping -> 200', (client, _) async {
final res = await client.getJson('/ping');
res.assertSuccess().assertJsonPath('message', 'pong');
}, handler: handler);
}

In-Memory API Smoke Test with Shelf

Keep things fast by using TransportMode.inMemory.

import 'dart:convert';

import 'package:shelf/shelf.dart' as shelf;
import 'package:server_testing/server_testing.dart';
import 'package:server_testing_shelf/server_testing_shelf.dart';

void main() {
final handler = ShelfRequestHandler((shelf.Request req) async {
final body = jsonEncode({'message': 'pong'});
return shelf.Response.ok(body, headers: {'content-type': 'application/json'});
});

serverTest('GET /ping -> 200', (client, _) async {
final res = await client.getJson('/ping');
res
..assertSuccess()
..assertJsonPath('message', 'pong');
}, handler: handler);
}

Ephemeral Server with Shared Fixtures

Spin up a real HttpServer per test, share seeded data, and clean up afterwards.

import 'package:server_testing/server_testing.dart';
import 'package:server_testing_shelf/server_testing_shelf.dart';
import 'package:test/test.dart';

late TestClient client;
late ShelfRequestHandler handler;

void main() {
setUpAll(() async {
await seedDatabase(); // your own helper
handler = ShelfRequestHandler(buildShelfApp());
client = TestClient(handler, mode: TransportMode.ephemeralServer);
});

tearDownAll(() async {
await client.close();
});

test('orders index reflects seeded data', () async {
final res = await client.get('/orders');
res.assertSuccess().assertJson((json) => json.count('orders', 2));
});
}

Each test hits a live socket, so always await client.close() once the suite finishes.

Custom Framework Adapter

Implement RequestHandler to adapt your own framework, then plug it into serverTest.

class RouterHandler implements RequestHandler {
final MyRouter router;
HttpServer? _server;

RouterHandler(this.router);


Future<void> handleRequest(HttpRequest request) {
return router.handle(HttpRequestWrapper.from(request));
}


Future<int> startServer({int port = 0}) async {
_server = await HttpServer.bind(InternetAddress.loopbackIPv4, port);
_server!.listen(handleRequest);
return _server!.port;
}


Future<void> close([bool force = true]) async {
await _server?.close(force: force);
_server = null;
}
}

void main() {
final handler = RouterHandler(buildRouter());

serverTest('router responds', (client, _) async {
final res = await client.get('/health');
res.assertStatus(200).assertBody('ok');
}, handler: handler);
}

CLI Workflow in CI

Use the server_testing CLI to provision browsers and drivers before running tests.

dart run server_testing install # download browser bundles
dart run server_testing install:driver # ensure WebDriver binary
dart test # execute your suite

Cache ~/.cache/server_testing between CI runs to avoid repeated downloads.