0
# System Mocking
1
2
Mock system-level operations including child process spawning for testing command-line interactions and process execution without actually running external commands.
3
4
## Capabilities
5
6
### Mock Child Process Spawn
7
8
Mock `child_process.spawn()` to simulate process execution with custom exit codes, stdout, and stderr output.
9
10
```typescript { .api }
11
/**
12
* Mock child_process.spawn with custom exit codes and output
13
* @param code - Exit code for the spawned process
14
* @param stdout - Standard output data to emit
15
* @param stderr - Standard error data to emit
16
* @param timeout - Delay before emitting events in milliseconds (default: 0)
17
*/
18
function spawn(code: number, stdout: string, stderr: string, timeout?: number): void;
19
```
20
21
**Usage Examples:**
22
23
```typescript
24
import { spawn as spawnMock } from "mm";
25
import { spawn } from "node:child_process";
26
27
// Mock successful command execution
28
spawnMock(0, "Command executed successfully", "");
29
30
const process = spawn("echo", ["hello world"]);
31
32
process.stdout.on("data", (data) => {
33
console.log(`stdout: ${data}`); // => "stdout: Command executed successfully"
34
});
35
36
process.stderr.on("data", (data) => {
37
console.log(`stderr: ${data}`); // Won't be called (empty stderr)
38
});
39
40
process.on("close", (code) => {
41
console.log(`Process exited with code: ${code}`); // => "Process exited with code: 0"
42
});
43
44
process.on("exit", (code) => {
45
console.log(`Process exit event: ${code}`); // => "Process exit event: 0"
46
});
47
```
48
49
### Mock Command Failures
50
51
Simulate command failures with non-zero exit codes and error output:
52
53
```typescript
54
import { spawn as spawnMock } from "mm";
55
import { spawn } from "node:child_process";
56
57
// Mock command failure
58
spawnMock(1, "", "Command not found");
59
60
const process = spawn("nonexistent-command", ["arg1", "arg2"]);
61
62
process.stdout.on("data", (data) => {
63
// Won't be called (empty stdout)
64
});
65
66
process.stderr.on("data", (data) => {
67
console.log(`Error: ${data}`); // => "Error: Command not found"
68
});
69
70
process.on("close", (code) => {
71
console.log(`Failed with code: ${code}`); // => "Failed with code: 1"
72
});
73
```
74
75
### Mock with Output and Errors
76
77
Simulate processes that produce both stdout and stderr output:
78
79
```typescript
80
import { spawn as spawnMock } from "mm";
81
import { spawn } from "node:child_process";
82
83
// Mock process with mixed output
84
spawnMock(0, "Processing files...\nCompleted successfully", "Warning: deprecated option used");
85
86
const process = spawn("some-tool", ["--process", "files"]);
87
88
process.stdout.on("data", (data) => {
89
console.log(`Output: ${data}`);
90
// => "Output: Processing files...
91
// Completed successfully"
92
});
93
94
process.stderr.on("data", (data) => {
95
console.log(`Warning: ${data}`);
96
// => "Warning: Warning: deprecated option used"
97
});
98
99
process.on("close", (code) => {
100
console.log(`Process completed with code: ${code}`); // => 0
101
});
102
```
103
104
### Mock with Timing
105
106
Simulate delayed process execution and output timing:
107
108
```typescript
109
import { spawn as spawnMock } from "mm";
110
import { spawn } from "node:child_process";
111
112
// Mock slow command with 2 second delay
113
spawnMock(0, "Long running task completed", "", 2000);
114
115
console.log("Starting process...");
116
const process = spawn("slow-command", []);
117
118
process.on("close", (code) => {
119
console.log("Process finished after delay"); // Logs after 2 seconds
120
});
121
```
122
123
## Event Emission Sequence
124
125
The mocked spawn process emits events in the following order:
126
127
1. **stdout event** (if stdout data provided)
128
2. **stderr event** (if stderr data provided)
129
3. **close event** (with exit code)
130
4. **exit event** (with exit code)
131
132
```typescript
133
import { spawn as spawnMock } from "mm";
134
import { spawn } from "node:child_process";
135
136
spawnMock(0, "output", "error");
137
138
const process = spawn("test-command", []);
139
140
process.stdout.on("data", () => console.log("1. stdout"));
141
process.stderr.on("data", () => console.log("2. stderr"));
142
process.on("close", () => console.log("3. close"));
143
process.on("exit", () => console.log("4. exit"));
144
145
// Output:
146
// 1. stdout
147
// 2. stderr
148
// 3. close
149
// 4. exit
150
```
151
152
## Integration Patterns
153
154
### Testing Command Line Tools
155
156
Mock command execution for testing CLI-dependent code:
157
158
```typescript
159
import { spawn as spawnMock, restore } from "mm";
160
161
class GitHelper {
162
async getCurrentBranch(): Promise<string> {
163
return new Promise((resolve, reject) => {
164
const process = spawn("git", ["branch", "--show-current"]);
165
let output = "";
166
167
process.stdout.on("data", (data) => {
168
output += data.toString();
169
});
170
171
process.on("close", (code) => {
172
if (code === 0) {
173
resolve(output.trim());
174
} else {
175
reject(new Error("Git command failed"));
176
}
177
});
178
});
179
}
180
}
181
182
// Test the GitHelper
183
async function testGitHelper() {
184
const gitHelper = new GitHelper();
185
186
// Mock successful git command
187
spawnMock(0, "main\n", "");
188
const branch = await gitHelper.getCurrentBranch();
189
console.log(branch); // => "main"
190
191
restore();
192
193
// Mock git command failure
194
spawnMock(128, "", "fatal: not a git repository");
195
try {
196
await gitHelper.getCurrentBranch();
197
} catch (err) {
198
console.log(err.message); // => "Git command failed"
199
}
200
}
201
```
202
203
### Mock Different Exit Codes
204
205
Test handling of various exit codes:
206
207
```typescript
208
import { spawn as spawnMock } from "mm";
209
210
// Success
211
spawnMock(0, "Success", "");
212
213
// General error
214
spawnMock(1, "", "General error");
215
216
// Command not found
217
spawnMock(127, "", "Command not found");
218
219
// Permission denied
220
spawnMock(126, "", "Permission denied");
221
222
// Interrupted (Ctrl+C)
223
spawnMock(130, "Partial output", "Interrupted");
224
```
225
226
### Mock Long-Running Processes
227
228
Simulate processes that take time to complete:
229
230
```typescript
231
import { spawn as spawnMock } from "mm";
232
233
class ProcessManager {
234
async runBuild(): Promise<void> {
235
return new Promise((resolve, reject) => {
236
console.log("Starting build...");
237
238
const process = spawn("npm", ["run", "build"]);
239
240
process.stdout.on("data", (data) => {
241
console.log(`Build: ${data}`);
242
});
243
244
process.on("close", (code) => {
245
if (code === 0) {
246
console.log("Build completed successfully");
247
resolve();
248
} else {
249
reject(new Error(`Build failed with code ${code}`));
250
}
251
});
252
});
253
}
254
}
255
256
// Test with delayed mock
257
async function testBuild() {
258
const manager = new ProcessManager();
259
260
// Mock build process with 3 second delay
261
spawnMock(0, "Building project...\nBuild successful!", "", 3000);
262
263
await manager.runBuild();
264
// Output after 3 seconds:
265
// Starting build...
266
// Build: Building project...
267
// Build successful!
268
// Build completed successfully
269
}
270
```
271
272
## Error Simulation Patterns
273
274
### Network-Related Command Failures
275
276
```typescript
277
import { spawn as spawnMock } from "mm";
278
279
// Mock network timeout
280
spawnMock(124, "", "curl: (28) Operation timed out");
281
282
// Mock DNS resolution failure
283
spawnMock(6, "", "curl: (6) Could not resolve host");
284
285
// Mock connection refused
286
spawnMock(7, "", "curl: (7) Failed to connect to host");
287
```
288
289
### File System Command Failures
290
291
```typescript
292
import { spawn as spawnMock } from "mm";
293
294
// Mock file not found
295
spawnMock(2, "", "cat: file.txt: No such file or directory");
296
297
// Mock permission denied
298
spawnMock(1, "", "mkdir: cannot create directory 'restricted': Permission denied");
299
300
// Mock disk full
301
spawnMock(1, "", "cp: error writing 'destination': No space left on device");
302
```
303
304
## Testing Process Event Handlers
305
306
Mock processes to test event handling code:
307
308
```typescript
309
import { spawn as spawnMock } from "mm";
310
311
class ProcessMonitor {
312
monitorProcess(command: string, args: string[]): Promise<ProcessResult> {
313
return new Promise((resolve) => {
314
const process = spawn(command, args);
315
const result: ProcessResult = {
316
stdout: "",
317
stderr: "",
318
exitCode: 0
319
};
320
321
process.stdout.on("data", (data) => {
322
result.stdout += data.toString();
323
});
324
325
process.stderr.on("data", (data) => {
326
result.stderr += data.toString();
327
});
328
329
process.on("close", (code) => {
330
result.exitCode = code;
331
resolve(result);
332
});
333
});
334
}
335
}
336
337
// Test the monitor
338
async function testMonitor() {
339
const monitor = new ProcessMonitor();
340
341
spawnMock(0, "Hello World", "Debug info");
342
343
const result = await monitor.monitorProcess("echo", ["Hello World"]);
344
console.log(result);
345
// => { stdout: "Hello World", stderr: "Debug info", exitCode: 0 }
346
}
347
```
348
349
## Types
350
351
```typescript { .api }
352
// Exit codes commonly used in process mocking
353
interface CommonExitCodes {
354
SUCCESS: 0;
355
GENERAL_ERROR: 1;
356
MISUSE_OF_SHELL_BUILTINS: 2;
357
PERMISSION_DENIED: 126;
358
COMMAND_NOT_FOUND: 127;
359
INVALID_EXIT_ARGUMENT: 128;
360
TERMINATED_BY_CTRL_C: 130;
361
}
362
363
// Process result structure for testing
364
interface ProcessResult {
365
stdout: string;
366
stderr: string;
367
exitCode: number;
368
}
369
```