Testing
Learn how to test your Aim applications with the official testing utilities.
Testing Package
Aim provides aim_server_testing package with helpers and matchers for testing your applications.
bash
dart pub add --dev aim_server_testingTestClient
The TestClient allows you to test your routes without starting a real server:
dart
import 'package:aim_server/aim_server.dart';
import 'package:aim_server_testing/aim_server_testing.dart';
import 'package:test/test.dart';
void main() {
test('GET / returns welcome message', () async {
final app = Aim();
app.get('/', (c) async {
return c.json({'message': 'Welcome'});
});
final client = TestClient(app);
final response = await client.get('/');
expect(response.statusCode, equals(200));
final body = await response.bodyAsJson();
expect(body['message'], equals('Welcome'));
});
}Making Requests
GET Request
dart
final response = await client.get('/users');POST Request
dart
final response = await client.post(
'/users',
body: {'name': 'Alice', 'email': 'alice@example.com'},
);With Headers
dart
final response = await client.get(
'/protected',
headers: {'Authorization': 'Bearer token123'},
);With Query Parameters
dart
final response = await client.get('/search?q=dart&page=2');Response Helpers
Status Code
dart
expect(response.statusCode, equals(200));
expect(response.statusCode, equals(201));
expect(response.statusCode, equals(404));JSON Body
dart
final body = await response.bodyAsJson();
expect(body['message'], equals('Success'));Text Body
dart
final text = await response.bodyAsString();
expect(text, contains('Hello'));Headers
dart
expect(response.headers['content-type'], equals('application/json'));
expect(response.headers['x-api-version'], equals('1.0'));Testing Middleware
dart
test('Logger middleware logs requests', () async {
final app = Aim();
app.use(logger());
app.get('/', (c) async => c.text('OK'));
final client = TestClient(app);
final response = await client.get('/');
expect(response.statusCode, equals(200));
});Testing Authentication
dart
import 'package:aim_server_jwt/aim_server_jwt.dart';
void main() {
late Aim<JwtVariables> app;
late TestClient client;
setUp(() {
app = Aim<JwtVariables>(
variablesFactory: () => JwtVariables.create(
JwtOptions(
algorithm: HS256(
secretKey: SecretKey(secret: 'test-secret-key-at-least-32-chars'),
),
excludedPaths: ['/login'],
),
),
);
app.use(jwt());
app.post('/login', (c) async {
final token = Jwt(options: c.variables.jwtOptions).sign({
'user_id': 123,
});
return c.json({'token': token});
});
app.get('/protected', (c) async {
final payload = c.variables.jwtPayload;
return c.json({'user_id': payload['user_id']});
});
client = TestClient(app);
});
test('Login returns token', () async {
final response = await client.post('/login');
expect(response.statusCode, equals(200));
final body = await response.bodyAsJson();
expect(body['token'], isNotNull);
});
test('Protected route requires auth', () async {
final response = await client.get('/protected');
expect(response.statusCode, equals(401));
});
test('Protected route with token succeeds', () async {
// Get token
final loginRes = await client.post('/login');
final loginBody = await loginRes.bodyAsJson();
final token = loginBody['token'];
// Access protected route
final response = await client.get(
'/protected',
headers: {'Authorization': 'Bearer $token'},
);
expect(response.statusCode, equals(200));
final body = await response.bodyAsJson();
expect(body['user_id'], equals(123));
});
}Testing CORS
dart
import 'package:aim_server_cors/aim_server_cors.dart';
test('CORS headers are set', () async {
final app = Aim();
app.use(cors(CorsOptions(
origin: 'https://example.com',
credentials: true,
)));
app.get('/api', (c) async => c.json({'data': 'value'}));
final client = TestClient(app);
final response = await client.get(
'/api',
headers: {'Origin': 'https://example.com'},
);
expect(
response.headers['access-control-allow-origin'],
equals('https://example.com'),
);
expect(
response.headers['access-control-allow-credentials'],
equals('true'),
);
});Testing Error Handling
dart
test('Returns 404 for unknown routes', () async {
final app = Aim();
app.get('/', (c) async => c.text('Home'));
app.notFound((c) async {
return c.json({'error': 'Not Found'}, statusCode: 404);
});
final client = TestClient(app);
final response = await client.get('/unknown');
expect(response.statusCode, equals(404));
final body = await response.bodyAsJson();
expect(body['error'], equals('Not Found'));
});Testing Form Data
formData() is an extension method on Request, not a middleware:
dart
import 'package:aim_server/aim_server.dart';
import 'package:aim_server_form/aim_server_form.dart';
import 'package:aim_server_testing/aim_server_testing.dart';
import 'package:test/test.dart';
test('Form data is parsed', () async {
final app = Aim();
app.post('/submit', (c) async {
final form = await c.req.formData();
final name = form['name'];
return c.json({'name': name});
});
final client = TestClient(app);
final response = await client.post(
'/submit',
headers: {'content-type': 'application/x-www-form-urlencoded'},
body: 'name=Alice',
);
expect(response.statusCode, equals(200));
final body = await response.bodyAsJson();
expect(body['name'], equals('Alice'));
});Best Practices
Use setUp and tearDown
dartlate Aim app; late TestClient client; setUp(() { app = Aim(); // Configure app... client = TestClient(app); });Test error cases
darttest('Returns 400 for invalid input', () async { final response = await client.post('/users', body: {}); expect(response.statusCode, equals(400)); });Test different HTTP methods
darttest('GET returns list', () async { ... }); test('POST creates resource', () async { ... }); test('PUT updates resource', () async { ... }); test('DELETE removes resource', () async { ... });Test middleware order
darttest('Auth middleware runs before handler', () async { ... });Test with realistic data
dartfinal userData = { 'name': 'Alice', 'email': 'alice@example.com', 'age': 30, };
Running Tests
bash
# Run all tests
dart test
# Run specific file
dart test test/routes_test.dart
# Run with coverage
dart test --coverage=coverage
dart install coverage
format_coverage --lcov --in=coverage --out=coverage/lcov.infoNext Steps
- Learn about Best Practices
- Explore the Context API
- Check out Middleware testing
