Joker ๐

HTTP request mocking for Dart & Flutter. Beyond testing - enable independent frontend development without backend dependencies.
Use Cases
๐ Independent Development
Build complete features before backend APIs exist:
void main() async {
if (kDebugMode) {
setupMockAPIs(); // Only in development
}
runApp(MyApp());
}
void setupMockAPIs() {
Joker.start();
// User authentication
Joker.stubJson(
host: 'api.myapp.com',
path: '/auth/login',
method: 'POST',
data: {
'token': 'dev_token_123',
'user': {'id': 1, 'name': 'Dev User'}
},
);
// Product catalog
Joker.stubJson(
host: 'api.myapp.com',
path: '/products',
data: {
'products': [
{'id': 1, 'name': 'iPhone 15', 'price': 999},
{'id': 2, 'name': 'MacBook Pro', 'price': 1999},
]
},
);
}
๐งช Testing Excellence
Reliable, fast tests with controlled responses:
group('User Profile Tests', () {
setUp(() => Joker.start());
tearDown(() => Joker.stop());
test('loads user profile successfully', () async {
Joker.stubJson(
host: 'api.myapp.com',
path: '/users/123',
data: {'id': 123, 'name': 'John Doe', 'email': '[email protected]'},
);
final profile = await userService.getProfile(123);
expect(profile.name, equals('John Doe'));
});
test('handles network errors gracefully', () async {
Joker.stubJson(
host: 'api.myapp.com',
path: '/users/456',
data: {'error': 'User not found'},
statusCode: 404,
);
expect(
() => userService.getProfile(456),
throwsA(isA<UserNotFound>())
);
});
});
Key Features
- โ
Universal: Works with any HTTP client using
HttpClient(http,dio, etc.) - ๐ฏ Smart Matching: Match by host, path, method, or any combination
- ๐ฆ JSON-First: Built-in JSON response handling
- โก Full Control: Custom status codes, headers, delays
- ๐ท๏ธ Organized: Named stubs for complex scenarios
- ๐ Flexible: One-time or persistent stubs
- ๐ซ Non-Invasive: No changes needed to existing code
Installation
Add to your pubspec.yaml:
dev_dependencies:
joker: ^0.2.0
๐ฑ Platform Support: This package is designed for native platforms (mobile, desktop, server) where it provides automatic HTTP interception via
HttpOverrides.๐ For Web or Cross-Platform: Use
joker_httporjoker_diowhich work on all platforms including web.
Quick Start
import 'package:joker/joker.dart';
import 'package:http/http.dart' as http;
// Start intercepting (required)
Joker.start();
// Simple JSON response
Joker.stubJson(
host: 'api.example.com',
path: '/users',
data: {'users': [{'id': 1, 'name': 'Alice'}]},
);
// Your existing code works unchanged!
final response = await http.get(Uri.parse('https://api.example.com/users'));
final users = jsonDecode(response.body)['users'];
// Always stop when done
Joker.stop();
๐ก Note: This automatic interception works on native platforms (mobile, desktop, server). For web or cross-platform apps, see
joker_httporjoker_dio.
Complete API Reference
Core Methods
| Method | Purpose |
|---|---|
Joker.start() |
Begin intercepting HTTP requests |
Joker.stop() |
Stop intercepting and clear all stubs |
Joker.stubJson({...}) |
Create JSON response stubs |
Joker.stubJsonArray({...}) |
Create JSON array response stubs |
Joker.stubText({...}) |
Create text response stubs |
Joker.stubJsonFile({...}) |
Create JSON stubs from file (async) - Native only |
Joker.removeStub(stub) |
Remove specific stub |
Joker.removeStubsByName(name) |
Remove stubs by name |
Joker.clearStubs() |
Remove all stubs |
Joker.stubs |
Get all registered stubs (read-only) |
Joker.isActive |
Check if Joker is intercepting requests |
Stubbing Methods
stubJson() - JSON Object Responses
Creates stubs that return JSON objects:
Joker.stubJson(
host: 'api.example.com',
path: '/user/profile',
method: 'GET',
data: {
'id': 123,
'name': 'John Doe',
'email': '[email protected]'
},
statusCode: 200,
headers: {'x-api-version': '1.0'},
delay: Duration(milliseconds: 300),
name: 'user-profile',
removeAfterUse: false,
);
stubJsonArray() - JSON Array Responses
Creates stubs that return JSON arrays at root level:
Joker.stubJsonArray(
host: 'api.example.com',
path: '/posts',
data: [
{'id': 1, 'title': 'Post 1', 'author': 'Alice'},
{'id': 2, 'title': 'Post 2', 'author': 'Bob'},
],
statusCode: 200,
headers: {'x-total-count': '2'},
);
stubText() - Plain Text Responses
Creates stubs that return plain text:
Joker.stubText(
host: 'api.example.com',
path: '/health',
text: 'OK',
statusCode: 200,
headers: {'content-type': 'text/plain'},
);
stubJsonFile() - Load JSON from File
Note: Only available on native platforms (mobile, desktop, server). Not available on web due to dart:io restrictions.
Creates stubs by loading JSON data from files (async):
await Joker.stubJsonFile(
host: 'api.example.com',
path: '/users',
filePath: 'test/fixtures/users.json',
statusCode: 200,
delay: Duration(milliseconds: 500),
);
Common Parameters
All stubbing methods share these parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
host |
String? |
null |
Host to match (e.g., 'api.example.com') |
path |
String? |
null |
Path to match (e.g., '/users') |
method |
String? |
null |
HTTP method ('GET', 'POST', etc.) |
statusCode |
int |
200 |
HTTP status code |
headers |
Map<String, String> |
{} |
Response headers |
delay |
Duration? |
null |
Artificial response delay |
name |
String? |
null |
Stub name for management |
removeAfterUse |
bool |
false |
Auto-remove after first match |
Method-Specific Parameters
stubJson() Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
data |
Map<String, dynamic> |
โ | JSON object to return |
stubJsonArray() Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
data |
List<Map<String, dynamic>> |
โ | JSON array to return |
stubText() Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
text |
String |
โ | Plain text content to return |
stubJsonFile() Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
filePath |
String |
โ | Path to JSON file to load |
Stub Management Examples
Basic Stubbing
// Start intercepting
Joker.start();
// Simple JSON response
Joker.stubJson(
host: 'api.example.com',
path: '/posts',
data: {'posts': [{'id': 1, 'title': 'Hello World'}]},
);
// Make request - it will be intercepted
final posts = await apiClient.getPosts();
Advanced Configuration
// POST endpoint with custom response
Joker.stubJson(
host: 'api.example.com',
path: '/users',
method: 'POST',
data: {'id': 42, 'created': true},
statusCode: 201,
headers: {'Location': '/users/42'},
delay: Duration(milliseconds: 300), // Simulate network latency
);
// Error responses for testing
Joker.stubJson(
host: 'api.example.com',
path: '/users/invalid',
data: {'error': 'Invalid user ID', 'code': 'INVALID_ID'},
statusCode: 400,
);
Dynamic Stub Management
// Named stubs for organization
final stub = Joker.stubJson(
host: 'api.example.com',
path: '/products',
data: {'products': []},
name: 'empty-catalog',
);
// Update stub data dynamically
Joker.removeStubsByName('empty-catalog');
Joker.stubJson(
host: 'api.example.com',
path: '/products',
data: {'products': [{'id': 1, 'name': 'New Product'}]},
name: 'populated-catalog',
);
// One-time stubs (auto-remove after use)
Joker.stubJson(
host: 'api.example.com',
path: '/auth/refresh',
data: {'token': 'new_token_456'},
removeAfterUse: true,
);
// Check active stubs
print('Active stubs: ${Joker.stubs.length}');
print('Joker is active: ${Joker.isActive}');
Real-World Development Example
class ApiMockService {
static void setupDevelopmentMocks() {
Joker.start();
_setupAuthEndpoints();
_setupUserEndpoints();
_setupProductEndpoints();
}
static void _setupAuthEndpoints() {
// Login success
Joker.stubJson(
host: 'api.myshop.com',
path: '/auth/login',
method: 'POST',
data: {
'success': true,
'token': 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...',
'user': {
'id': 1,
'email': '[email protected]',
'role': 'admin'
}
},
name: 'login-success',
);
}
static void _setupProductEndpoints() {
// Product list with pagination
Joker.stubJson(
host: 'api.myshop.com',
path: '/products',
data: {
'products': List.generate(20, (i) => {
'id': i + 1,
'name': 'Product ${i + 1}',
'price': (i + 1) * 10.99,
'image': 'https://picsum.photos/200/200?random=$i'
}),
'total': 100,
'page': 1,
'hasMore': true,
},
delay: Duration(milliseconds: 500), // Realistic loading time
);
}
}
Best Practices
Development Workflow
- Use
kDebugModeto enable mocks only in development - Create realistic mock data that matches your backend schema
- Add delays to test loading states and user experience
- Use named stubs to swap between different scenarios
Testing
- Always call
Joker.stop()intearDown()methods - Clear stubs between tests with
Joker.clearStubs() - Test both success and error scenarios
- Use
removeAfterUse: truefor one-time authentication flows
Organization
// Good: Organized by feature
void setupUserMocks() {
// user-related stubs
}
void setupProductMocks() {
// product-related stubs
}
void setupPaymentMocks() {
// payment-related stubs
}
// Good: Environment-specific setup
void setupMocksForTesting() {
// minimal, predictable data
}
void setupMocksForDemo() {
// rich, realistic data
}
How It Works
Joker uses Dart's HttpOverrides.global to intercept all HTTP requests made through HttpClient on native platforms. This works transparently with any package that uses the standard Dart HTTP stack:
package:httppackage:diodart:ioHttpClient- Most other HTTP packages
No changes to your existing code required - just start Joker and define your stubs!
Cross-Platform Support
For web or cross-platform applications, use the companion packages:
joker_http- Forpackage:httpon all platforms including webjoker_dio- Forpackage:dioon all platforms including web
These packages provide explicit client configuration that works across all platforms.
License
MIT Licensed - see LICENSE for details.
Support the Project
If you find joker helpful for your projects and it has saved you time, consider supporting its development with a coffee!
Every contribution is highly appreciated and motivates me to keep improving the library, adding new features, and providing support.
Libraries
- joker
- A powerful HTTP request stubbing and mocking library for Dart.