platform/packages/process/doc/testing.md

229 lines
5 KiB
Markdown
Raw Normal View History

# Testing Utilities
The Process package provides comprehensive testing utilities for process-dependent code.
## Process Faking
### Basic Faking
```dart
final factory = Factory();
// Fake specific commands
factory.fake({
'ls': 'file1.txt\nfile2.txt',
'cat file1.txt': 'Hello, World!',
'grep pattern': (process) => 'Matched line',
});
// Run fake processes
final result = await factory.command('ls').run();
expect(result.output().trim(), equals('file1.txt\nfile2.txt'));
```
### Preventing Real Processes
```dart
// Prevent any real process execution
factory.fake().preventStrayProcesses();
// This will throw an exception
await factory.command('real-command').run();
```
### Dynamic Results
```dart
factory.fake({
'random': (process) =>
DateTime.now().millisecondsSinceEpoch.toString(),
'conditional': (process) =>
process.env['SUCCESS'] == 'true' ? 'success' : 'failure',
});
```
## Process Descriptions
### Basic Description
```dart
final description = FakeProcessDescription()
..withExitCode(0)
..replaceOutput('Test output')
..replaceErrorOutput('Test error');
factory.fake({
'test-command': description,
});
```
### Simulating Long-Running Processes
```dart
final description = FakeProcessDescription()
..withOutputSequence(['Step 1', 'Step 2', 'Step 3'])
..withDelay(Duration(milliseconds: 100))
..runsFor(duration: Duration(seconds: 1));
factory.fake({
'long-task': description,
});
```
### Simulating Process Failures
```dart
final description = FakeProcessDescription()
..withExitCode(1)
..replaceOutput('Operation failed')
..replaceErrorOutput('Error: Invalid input');
factory.fake({
'failing-task': description,
});
```
## Process Sequences
### Basic Sequences
```dart
final sequence = FakeProcessSequence()
..then(FakeProcessResult(output: 'First'))
..then(FakeProcessResult(output: 'Second'))
..then(FakeProcessResult(output: 'Third'));
factory.fake({
'sequential-task': sequence,
});
```
### Alternating Success/Failure
```dart
final sequence = FakeProcessSequence.alternating(3);
while (sequence.hasMore) {
final result = sequence.call() as FakeProcessResult;
print('Success: ${result.successful()}');
}
```
### Custom Sequences
```dart
final sequence = FakeProcessSequence.fromOutputs([
'Starting...',
'Processing...',
'Complete!',
]);
factory.fake({
'progress-task': sequence,
});
```
## Testing Process Pools
```dart
test('executes processes concurrently', () async {
factory.fake({
'task1': FakeProcessDescription()
..withDelay(Duration(seconds: 1))
..replaceOutput('Result 1'),
'task2': FakeProcessDescription()
..withDelay(Duration(seconds: 1))
..replaceOutput('Result 2'),
});
final results = await factory.pool((pool) {
pool.command('task1');
pool.command('task2');
}).start();
expect(results.successful(), isTrue);
expect(results.total, equals(2));
});
```
## Testing Process Pipes
```dart
test('pipes output between processes', () async {
factory.fake({
'generate': 'initial data',
'transform': (process) => process.input.toUpperCase(),
'filter': (process) => process.input.contains('DATA') ? process.input : '',
});
final result = await factory.pipeThrough((pipe) {
pipe.command('generate');
pipe.command('transform');
pipe.command('filter');
}).run();
expect(result.output(), equals('INITIAL DATA'));
});
```
## Best Practices
1. Use `preventStrayProcesses()` in tests to catch unintended process execution
2. Simulate realistic scenarios with delays and sequences
3. Test both success and failure cases
4. Test process configuration (environment, working directory, etc.)
5. Test process coordination (pools and pipes)
6. Use process descriptions for complex behaviors
7. Test timeout and error handling
8. Mock system-specific behaviors
9. Clean up resources in tests
10. Test real-time output handling
## Example Test Suite
```dart
void main() {
group('Process Manager', () {
late Factory factory;
setUp(() {
factory = Factory();
factory.fake().preventStrayProcesses();
});
test('handles successful process', () async {
factory.fake({
'successful-task': FakeProcessDescription()
..withExitCode(0)
..replaceOutput('Success!'),
});
final result = await factory
.command('successful-task')
.run();
expect(result.successful(), isTrue);
expect(result.output(), equals('Success!'));
});
test('handles process failure', () async {
factory.fake({
'failing-task': FakeProcessDescription()
..withExitCode(1)
..replaceErrorOutput('Failed!'),
});
final result = await factory
.command('failing-task')
.run();
expect(result.failed(), isTrue);
expect(result.errorOutput(), equals('Failed!'));
});
});
}
```
For more information, see:
- [Process Execution](execution.md) for basic process management
- [Process Coordination](coordination.md) for pools and pipes