|
1 | 1 | import 'package:command_it/command_it.dart'; |
2 | 2 | import 'package:flutter_test/flutter_test.dart'; |
| 3 | +import 'package:get_it/get_it.dart'; |
| 4 | + |
| 5 | +final getIt = GetIt.instance; |
| 6 | + |
| 7 | +// Stub for example |
| 8 | +class Item { |
| 9 | + final String id; |
| 10 | + final String name; |
| 11 | + Item(this.id, this.name); |
| 12 | +} |
| 13 | + |
| 14 | +class ApiClient { |
| 15 | + Future<List<Item>> search(String query) async { |
| 16 | + await Future.delayed(Duration(milliseconds: 100)); |
| 17 | + return [Item('1', 'Result for $query')]; |
| 18 | + } |
| 19 | +} |
3 | 20 |
|
4 | 21 | // #region example |
5 | | -/// Example service that uses a command (for dependency injection in tests) |
| 22 | +/// Real service with actual command |
6 | 23 | class DataService { |
7 | | - final Command<void, List<String>> loadCommand; |
| 24 | + late final loadCommand = Command.createAsync<String, List<Item>>( |
| 25 | + (query) => getIt<ApiClient>().search(query), |
| 26 | + initialValue: [], |
| 27 | + ); |
| 28 | +} |
8 | 29 |
|
9 | | - DataService({required this.loadCommand}); |
| 30 | +/// Mock service for testing - overrides command with MockCommand |
| 31 | +class MockDataService implements DataService { |
| 32 | + @override |
| 33 | + late final loadCommand = _mockCommand; |
10 | 34 |
|
11 | | - void loadData() { |
12 | | - loadCommand.run(); |
| 35 | + final _mockCommand = MockCommand<String, List<Item>>( |
| 36 | + initialValue: [], |
| 37 | + ); |
| 38 | + |
| 39 | + // Control methods make tests readable and maintainable |
| 40 | + void simulateLoading() { |
| 41 | + _mockCommand.startExecution(); |
| 42 | + } |
| 43 | + |
| 44 | + void simulateSuccess(List<Item> data) { |
| 45 | + _mockCommand.endExecutionWithData(data); |
| 46 | + } |
| 47 | + |
| 48 | + void simulateError(String message) { |
| 49 | + _mockCommand.endExecutionWithError(message); |
13 | 50 | } |
14 | 51 | } |
15 | 52 |
|
16 | | -void main() { |
17 | | - group('MockCommand Examples', () { |
18 | | - test('Queue results with CommandResult', () async { |
19 | | - final mockLoadCommand = MockCommand<void, List<String>>( |
20 | | - initialValue: [], |
21 | | - ); |
22 | | - |
23 | | - // Queue results for the next execution using CommandResult |
24 | | - mockLoadCommand.queueResultsForNextExecuteCall([ |
25 | | - CommandResult<void, List<String>>( |
26 | | - null, ['Item 1', 'Item 2', 'Item 3'], null, false), |
27 | | - ]); |
| 53 | +// Code that depends on DataService |
| 54 | +class DataManager { |
| 55 | + DataManager() { |
| 56 | + // Listen to service command and update local state |
| 57 | + getIt<DataService>().loadCommand.isRunning.listen((running, _) { |
| 58 | + _isLoading = running; |
| 59 | + }); |
28 | 60 |
|
29 | | - // Inject into service |
30 | | - final service = DataService(loadCommand: mockLoadCommand); |
| 61 | + getIt<DataService>().loadCommand.listen((data, _) { |
| 62 | + _currentData = data; |
| 63 | + }); |
| 64 | + } |
31 | 65 |
|
32 | | - // Trigger the command |
33 | | - service.loadData(); |
| 66 | + bool _isLoading = false; |
| 67 | + bool get isLoading => _isLoading; |
34 | 68 |
|
35 | | - // Verify the command was called |
36 | | - expect(mockLoadCommand.executionCount, 1); |
| 69 | + List<Item> _currentData = []; |
| 70 | + List<Item> get currentData => _currentData; |
37 | 71 |
|
38 | | - // Verify the result |
39 | | - expect(mockLoadCommand.value, ['Item 1', 'Item 2', 'Item 3']); |
40 | | - }); |
| 72 | + Future<void> loadData(String query) async { |
| 73 | + getIt<DataService>().loadCommand(query); |
| 74 | + } |
| 75 | +} |
41 | 76 |
|
42 | | - test('Manually control execution states', () { |
43 | | - final mockCommand = MockCommand<void, String>( |
44 | | - initialValue: '', |
45 | | - ); |
| 77 | +void main() { |
| 78 | + group('MockCommand Pattern', () { |
| 79 | + test('Test manager with mock service - success state', () async { |
| 80 | + final mockService = MockDataService(); |
| 81 | + getIt.registerSingleton<DataService>(mockService); |
46 | 82 |
|
47 | | - // Initially not running |
48 | | - expect(mockCommand.isRunning.value, false); |
| 83 | + final manager = DataManager(); |
49 | 84 |
|
50 | | - // Start execution manually |
51 | | - mockCommand.startExecution(); |
52 | | - expect(mockCommand.isRunning.value, true); |
| 85 | + final testData = [Item('1', 'Test Item')]; |
53 | 86 |
|
54 | | - // Complete execution with data |
55 | | - mockCommand.endExecutionWithData('loaded data'); |
56 | | - expect(mockCommand.isRunning.value, false); |
57 | | - expect(mockCommand.value, 'loaded data'); |
58 | | - }); |
| 87 | + // Queue result for the next execution |
| 88 | + mockService._mockCommand.queueResultsForNextExecuteCall([ |
| 89 | + CommandResult<String, List<Item>>('test', testData, null, false), |
| 90 | + ]); |
59 | 91 |
|
60 | | - test('Simulate error scenarios', () { |
61 | | - final mockCommand = MockCommand<void, String>( |
62 | | - initialValue: '', |
63 | | - ); |
| 92 | + // Execute the command through the manager |
| 93 | + await manager.loadData('test'); |
64 | 94 |
|
65 | | - CommandError? capturedError; |
66 | | - mockCommand.errors.listen((error, _) => capturedError = error); |
| 95 | + // Wait for listener to fire |
| 96 | + await Future.delayed(Duration.zero); |
67 | 97 |
|
68 | | - // Simulate error with String message (not Exception) |
69 | | - mockCommand.startExecution(); |
70 | | - mockCommand.endExecutionWithError('Network error'); |
| 98 | + // Verify success state |
| 99 | + expect(manager.isLoading, false); |
| 100 | + expect(manager.currentData, testData); |
71 | 101 |
|
72 | | - expect(capturedError?.error.toString(), contains('Network error')); |
73 | | - expect(mockCommand.isRunning.value, false); |
| 102 | + // Cleanup |
| 103 | + await getIt.reset(); |
74 | 104 | }); |
75 | 105 |
|
76 | | - test('Complete execution without data (void commands)', () { |
77 | | - final mockCommand = MockCommand<void, void>( |
78 | | - initialValue: null, |
79 | | - noReturnValue: true, |
80 | | - ); |
81 | | - |
82 | | - var executionCompleted = false; |
83 | | - mockCommand.results.listen((result, _) { |
84 | | - if (!result.isRunning && !result.hasError) { |
85 | | - executionCompleted = true; |
86 | | - } |
| 106 | + test('Test manager with mock service - error state', () async { |
| 107 | + final mockService = MockDataService(); |
| 108 | + getIt.registerSingleton<DataService>(mockService); |
| 109 | + |
| 110 | + final manager = DataManager(); |
| 111 | + |
| 112 | + CommandError? capturedError; |
| 113 | + mockService.loadCommand.errors.listen((error, _) { |
| 114 | + capturedError = error; |
87 | 115 | }); |
88 | 116 |
|
89 | | - mockCommand.startExecution(); |
90 | | - mockCommand.endExecutionNoData(); |
| 117 | + // Simulate error without using loadData |
| 118 | + mockService.simulateError('Network error'); |
| 119 | + |
| 120 | + // Wait for listener to fire |
| 121 | + await Future.delayed(Duration.zero); |
91 | 122 |
|
92 | | - expect(executionCompleted, true); |
93 | | - expect(mockCommand.isRunning.value, false); |
| 123 | + // Verify error state |
| 124 | + expect(manager.isLoading, false); |
| 125 | + expect(capturedError?.error.toString(), contains('Network error')); |
| 126 | + |
| 127 | + // Cleanup |
| 128 | + await getIt.reset(); |
94 | 129 | }); |
95 | 130 |
|
96 | | - test('Track execution count', () { |
97 | | - final mockCommand = MockCommand<String, void>( |
98 | | - initialValue: null, |
99 | | - noReturnValue: true, |
100 | | - ); |
| 131 | + test('Real service works as expected', () async { |
| 132 | + // Register real dependencies |
| 133 | + getIt.registerSingleton<ApiClient>(ApiClient()); |
| 134 | + getIt.registerSingleton<DataService>(DataService()); |
| 135 | + |
| 136 | + final manager = DataManager(); |
| 137 | + |
| 138 | + // Test with real service |
| 139 | + await manager.loadData('flutter'); |
101 | 140 |
|
102 | | - expect(mockCommand.executionCount, 0); |
| 141 | + await Future.delayed(Duration(milliseconds: 150)); |
103 | 142 |
|
104 | | - mockCommand.run('test1'); |
105 | | - expect(mockCommand.executionCount, 1); |
106 | | - expect(mockCommand.lastPassedValueToExecute, 'test1'); |
| 143 | + expect(manager.currentData.isNotEmpty, true); |
| 144 | + expect(manager.currentData.first.name, contains('flutter')); |
107 | 145 |
|
108 | | - mockCommand.run('test2'); |
109 | | - expect(mockCommand.executionCount, 2); |
110 | | - expect(mockCommand.lastPassedValueToExecute, 'test2'); |
| 146 | + // Cleanup |
| 147 | + await getIt.reset(); |
111 | 148 | }); |
112 | 149 | }); |
113 | 150 | } |
|
0 commit comments