Joker ๐Ÿƒ

Joker Banner

Pub Version License: MIT

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': 'john@example.com'},
    );

    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_http or joker_dio which 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_http or joker_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': 'john@example.com'
  },
  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': 'dev@example.com', 
          '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 kDebugMode to 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() in tearDown() methods
  • Clear stubs between tests with Joker.clearStubs()
  • Test both success and error scenarios
  • Use removeAfterUse: true for 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:http
  • package:dio
  • dart:io HttpClient
  • 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 - For package:http on all platforms including web
  • joker_dio - For package:dio on 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.