Docs
You are viewing an older version (v1.0.0-alpha.1).
Go to the latest.
HTTP Tests
- Introduction
- Making Requests
- Testing JSON APIs
- Testing Responses
- Mocking HTTP
- Authentication Testing
Introduction
Magic provides utilities for testing HTTP interactions in your application. Test your API calls, mock responses, and verify your application handles various HTTP scenarios correctly.
Making Requests
Test HTTP Client
Set up a mock HTTP client for testing:
import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart';
class MockHttpClient extends Mock implements MagicHttpClient {}
void main() {
late MockHttpClient mockHttp;
setUp(() {
mockHttp = MockHttpClient();
Http.setTestClient(mockHttp);
});
tearDown(() {
Http.resetClient();
});
}
Testing JSON APIs
Mocking GET Requests
test('fetches users from API', () async {
// Arrange
when(() => mockHttp.get('/users'))
.thenAnswer((_) async => MagicResponse(
statusCode: 200,
body: [
{'id': 1, 'name': 'John'},
{'id': 2, 'name': 'Jane'},
],
));
// Act
final users = await UserController().fetchUsers();
// Assert
expect(users.length, equals(2));
expect(users.first.name, equals('John'));
verify(() => mockHttp.get('/users')).called(1);
});
Mocking POST Requests
test('creates user via API', () async {
final userData = {'name': 'John', 'email': '[email protected]'};
when(() => mockHttp.post('/users', data: userData))
.thenAnswer((_) async => MagicResponse(
statusCode: 201,
body: {'id': 1, ...userData},
));
final user = await UserController().createUser(userData);
expect(user.id, equals(1));
expect(user.name, equals('John'));
});
Testing Responses
Testing Success Responses
test('handles successful response', () async {
when(() => mockHttp.get('/status'))
.thenAnswer((_) async => MagicResponse(
statusCode: 200,
body: {'status': 'ok'},
));
final response = await Http.get('/status');
expect(response.successful, isTrue);
expect(response['status'], equals('ok'));
});
Testing Error Responses
test('handles 404 not found', () async {
when(() => mockHttp.get('/users/999'))
.thenAnswer((_) async => MagicResponse(
statusCode: 404,
body: {'message': 'User not found'},
));
final response = await Http.get('/users/999');
expect(response.notFound, isTrue);
expect(response.errorMessage, equals('User not found'));
});
Testing Validation Errors
test('handles validation errors', () async {
when(() => mockHttp.post('/users', data: any(named: 'data')))
.thenAnswer((_) async => MagicResponse(
statusCode: 422,
body: {
'message': 'The given data was invalid.',
'errors': {
'email': ['The email field is required.'],
'password': ['The password must be at least 8 characters.'],
},
},
));
final response = await Http.post('/users', data: {});
expect(response.isValidationError, isTrue);
expect(response.errors['email'], contains('The email field is required.'));
});
Mocking HTTP
Creating Mock Responses
MagicResponse mockSuccess(dynamic body) {
return MagicResponse(statusCode: 200, body: body);
}
MagicResponse mockValidationError(Map> errors) {
return MagicResponse(
statusCode: 422,
body: {
'message': 'Validation failed',
'errors': errors,
},
);
}
MagicResponse mockUnauthorized() {
return MagicResponse(
statusCode: 401,
body: {'message': 'Unauthenticated'},
);
}
Using Mock Helpers
test('controller handles validation error', () async {
when(() => mockHttp.post('/register', data: any(named: 'data')))
.thenAnswer((_) async => mockValidationError({
'email': ['Email already taken'],
}));
final controller = AuthController();
await controller.register({'email': '[email protected]'});
expect(controller.hasError('email'), isTrue);
expect(controller.getError('email'), equals('Email already taken'));
});
Authentication Testing
Testing Login Flow
test('successful login stores token and redirects', () async {
when(() => mockHttp.post('/login', data: any(named: 'data')))
.thenAnswer((_) async => MagicResponse(
statusCode: 200,
body: {
'token': 'test-token',
'user': {'id': 1, 'name': 'John'},
},
));
final controller = AuthController();
await controller.login({
'email': '[email protected]',
'password': 'password',
});
expect(Auth.check(), isTrue);
expect(Auth.user()?.name, equals('John'));
});
Testing Token Refresh
test('refreshes token on 401', () async {
// First request fails with 401
when(() => mockHttp.get('/protected'))
.thenAnswer((_) async => MagicResponse(statusCode: 401));
// Refresh succeeds
when(() => mockHttp.post('/refresh', data: any(named: 'data')))
.thenAnswer((_) async => MagicResponse(
statusCode: 200,
body: {'token': 'new-token'},
));
final success = await Auth.refreshToken();
expect(success, isTrue);
});
Testing Protected Routes
test('redirects unauthenticated user to login', () async {
// Ensure user is logged out
await Auth.logout();
final middleware = EnsureAuthenticated();
var redirected = false;
await middleware.handle(() {
// Should not reach here
fail('Should have redirected');
});
// Verify redirect happened
// (actual implementation would check navigation)
});
[!TIP] Create a
test/mocks/directory with reusable mock classes and response factories to keep tests DRY.