# Jest Advanced CLI Testing Example Advanced testing patterns for CLI applications including mocking, fixtures, and integration tests. ## Advanced Patterns ### 1. Async Command Testing ```typescript import { spawn } from 'child_process'; async function runCLIAsync(args: string[]): Promise { return new Promise((resolve) => { const child = spawn(CLI_PATH, args, { stdio: 'pipe' }); let stdout = ''; let stderr = ''; child.stdout?.on('data', (data) => { stdout += data.toString(); }); child.stderr?.on('data', (data) => { stderr += data.toString(); }); child.on('close', (code) => { resolve({ stdout, stderr, code: code || 0 }); }); }); } test('should handle long-running command', async () => { const result = await runCLIAsync(['deploy', 'production']); expect(result.code).toBe(0); }, 30000); // 30 second timeout ``` ### 2. Environment Variable Mocking ```typescript describe('environment configuration', () => { const originalEnv = { ...process.env }; afterEach(() => { process.env = { ...originalEnv }; }); test('should use API key from environment', () => { process.env.API_KEY = 'test_key_123'; const { stdout, code } = runCLI('status'); expect(code).toBe(0); expect(stdout).toContain('Authenticated'); }); test('should fail without API key', () => { delete process.env.API_KEY; const { stderr, code } = runCLI('status'); expect(code).toBe(1); expect(stderr).toContain('API key not found'); }); }); ``` ### 3. File System Fixtures ```typescript import fs from 'fs'; import os from 'os'; describe('config file handling', () => { let tempDir: string; beforeEach(() => { tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'cli-test-')); }); afterEach(() => { fs.rmSync(tempDir, { recursive: true, force: true }); }); test('should create config file', () => { const configFile = path.join(tempDir, '.config'); const result = runCLI(`init --config ${configFile}`); expect(result.code).toBe(0); expect(fs.existsSync(configFile)).toBe(true); const config = JSON.parse(fs.readFileSync(configFile, 'utf8')); expect(config).toHaveProperty('api_key'); }); }); ``` ### 4. Mocking External APIs ```typescript import nock from 'nock'; describe('API interaction', () => { beforeEach(() => { nock.cleanAll(); }); test('should fetch deployment status', () => { nock('https://api.example.com') .get('/deployments/123') .reply(200, { status: 'success', environment: 'production' }); const { stdout, code } = runCLI('status --deployment 123'); expect(code).toBe(0); expect(stdout).toContain('success'); expect(stdout).toContain('production'); }); test('should handle API errors', () => { nock('https://api.example.com') .get('/deployments/123') .reply(500, { error: 'Internal Server Error' }); const { stderr, code } = runCLI('status --deployment 123'); expect(code).toBe(1); expect(stderr).toContain('API error'); }); }); ``` ### 5. Test Fixtures ```typescript // test-fixtures.ts export const createTestFixtures = () => { const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'cli-test-')); // Create sample project structure fs.mkdirSync(path.join(tempDir, 'src')); fs.writeFileSync( path.join(tempDir, 'package.json'), JSON.stringify({ name: 'test-project', version: '1.0.0' }) ); return { tempDir, cleanup: () => fs.rmSync(tempDir, { recursive: true, force: true }), }; }; // Usage in tests test('should build project', () => { const fixtures = createTestFixtures(); try { const result = runCLI(`build --cwd ${fixtures.tempDir}`); expect(result.code).toBe(0); expect(fs.existsSync(path.join(fixtures.tempDir, 'dist'))).toBe(true); } finally { fixtures.cleanup(); } }); ``` ### 6. Snapshot Testing ```typescript test('help output matches snapshot', () => { const { stdout } = runCLI('--help'); expect(stdout).toMatchSnapshot(); }); test('version format matches snapshot', () => { const { stdout } = runCLI('--version'); expect(stdout).toMatchSnapshot(); }); ``` ### 7. Parameterized Tests ```typescript describe.each([ ['development', 'dev.example.com'], ['staging', 'staging.example.com'], ['production', 'api.example.com'], ])('deploy to %s', (environment, expectedUrl) => { test(`should deploy to ${environment}`, () => { const { stdout, code } = runCLI(`deploy ${environment}`); expect(code).toBe(0); expect(stdout).toContain(expectedUrl); }); }); ``` ### 8. Interactive Command Testing ```typescript import { Readable, Writable } from 'stream'; test('should handle interactive prompts', (done) => { const child = spawn(CLI_PATH, ['init'], { stdio: 'pipe' }); const inputs = ['my-project', 'John Doe', 'john@example.com']; let inputIndex = 0; child.stdout?.on('data', (data) => { const output = data.toString(); if (output.includes('?') && inputIndex < inputs.length) { child.stdin?.write(inputs[inputIndex] + '\n'); inputIndex++; } }); child.on('close', (code) => { expect(code).toBe(0); done(); }); }); ``` ### 9. Coverage-Driven Testing ```typescript // Ensure all CLI commands are tested describe('CLI command coverage', () => { const commands = ['init', 'build', 'deploy', 'status', 'config']; commands.forEach((command) => { test(`${command} command exists`, () => { const { stdout } = runCLI('--help'); expect(stdout).toContain(command); }); test(`${command} has help text`, () => { const { stdout, code } = runCLI(`${command} --help`); expect(code).toBe(0); expect(stdout).toContain('Usage:'); }); }); }); ``` ### 10. Performance Testing ```typescript test('command executes within time limit', () => { const startTime = Date.now(); const { code } = runCLI('status'); const duration = Date.now() - startTime; expect(code).toBe(0); expect(duration).toBeLessThan(2000); // Should complete within 2 seconds }); ``` ## Best Practices 1. **Use Test Fixtures**: Create reusable test data and cleanup functions 2. **Mock External Dependencies**: Never make real API calls or database connections 3. **Test Edge Cases**: Test boundary conditions, empty inputs, special characters 4. **Async Handling**: Use proper async/await or promises for async operations 5. **Cleanup**: Always cleanup temp files, reset mocks, restore environment 6. **Isolation**: Tests should not depend on execution order 7. **Clear Error Messages**: Write assertions with helpful failure messages ## Common Advanced Patterns - Concurrent execution testing - File locking and race conditions - Signal handling (SIGTERM, SIGINT) - Large file processing - Streaming output - Progress indicators - Error recovery and retry logic ## Resources - [Jest Advanced Features](https://jestjs.io/docs/advanced) - [Mocking with Jest](https://jestjs.io/docs/mock-functions) - [Snapshot Testing](https://jestjs.io/docs/snapshot-testing)