Initial commit
This commit is contained in:
145
skills/cli-testing-patterns/examples/jest-basic/README.md
Normal file
145
skills/cli-testing-patterns/examples/jest-basic/README.md
Normal file
@@ -0,0 +1,145 @@
|
||||
# Jest Basic CLI Testing Example
|
||||
|
||||
This example demonstrates basic CLI testing patterns using Jest for Node.js/TypeScript projects.
|
||||
|
||||
## Setup
|
||||
|
||||
```bash
|
||||
npm install --save-dev jest @types/jest ts-jest @types/node
|
||||
```
|
||||
|
||||
## Test Structure
|
||||
|
||||
```typescript
|
||||
import { execSync } from 'child_process';
|
||||
import path from 'path';
|
||||
|
||||
describe('CLI Tool Tests', () => {
|
||||
const CLI_PATH = path.join(__dirname, '../bin/mycli');
|
||||
|
||||
function runCLI(args: string) {
|
||||
try {
|
||||
const stdout = execSync(`${CLI_PATH} ${args}`, {
|
||||
encoding: 'utf8',
|
||||
stdio: 'pipe',
|
||||
});
|
||||
return { stdout, stderr: '', code: 0 };
|
||||
} catch (error: any) {
|
||||
return {
|
||||
stdout: error.stdout || '',
|
||||
stderr: error.stderr || '',
|
||||
code: error.status || 1,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
test('should display version', () => {
|
||||
const { stdout, code } = runCLI('--version');
|
||||
expect(code).toBe(0);
|
||||
expect(stdout).toContain('1.0.0');
|
||||
});
|
||||
|
||||
test('should display help', () => {
|
||||
const { stdout, code } = runCLI('--help');
|
||||
expect(code).toBe(0);
|
||||
expect(stdout).toContain('Usage:');
|
||||
});
|
||||
|
||||
test('should handle unknown command', () => {
|
||||
const { stderr, code } = runCLI('unknown-command');
|
||||
expect(code).toBe(1);
|
||||
expect(stderr).toContain('unknown command');
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
## Running Tests
|
||||
|
||||
```bash
|
||||
# Run all tests
|
||||
npm test
|
||||
|
||||
# Run with coverage
|
||||
npm run test:coverage
|
||||
|
||||
# Run in watch mode
|
||||
npm run test:watch
|
||||
```
|
||||
|
||||
## Key Patterns
|
||||
|
||||
### 1. Command Execution Helper
|
||||
|
||||
Create a reusable `runCLI()` function that:
|
||||
- Executes CLI commands using `execSync`
|
||||
- Captures stdout, stderr, and exit codes
|
||||
- Handles both success and failure cases
|
||||
|
||||
### 2. Exit Code Testing
|
||||
|
||||
Always test exit codes:
|
||||
- `0` for success
|
||||
- Non-zero for errors
|
||||
- Specific codes for different error types
|
||||
|
||||
### 3. Output Validation
|
||||
|
||||
Test output content using Jest matchers:
|
||||
- `.toContain()` for substring matching
|
||||
- `.toMatch()` for regex patterns
|
||||
- `.toBe()` for exact matches
|
||||
|
||||
### 4. Error Handling
|
||||
|
||||
Test error scenarios:
|
||||
- Unknown commands
|
||||
- Invalid options
|
||||
- Missing required arguments
|
||||
- Invalid argument types
|
||||
|
||||
## Example Test Cases
|
||||
|
||||
```typescript
|
||||
describe('deploy command', () => {
|
||||
test('should deploy with valid arguments', () => {
|
||||
const { stdout, code } = runCLI('deploy production --force');
|
||||
expect(code).toBe(0);
|
||||
expect(stdout).toContain('Deploying to production');
|
||||
});
|
||||
|
||||
test('should fail without required arguments', () => {
|
||||
const { stderr, code } = runCLI('deploy');
|
||||
expect(code).toBe(1);
|
||||
expect(stderr).toContain('missing required argument');
|
||||
});
|
||||
|
||||
test('should validate environment names', () => {
|
||||
const { stderr, code } = runCLI('deploy invalid-env');
|
||||
expect(code).toBe(1);
|
||||
expect(stderr).toContain('invalid environment');
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Isolate Tests**: Each test should be independent
|
||||
2. **Use Descriptive Names**: Test names should describe what they validate
|
||||
3. **Test Both Success and Failure**: Cover happy path and error cases
|
||||
4. **Mock External Dependencies**: Don't make real API calls or file system changes
|
||||
5. **Use Type Safety**: Leverage TypeScript for better test reliability
|
||||
6. **Keep Tests Fast**: Fast tests encourage frequent running
|
||||
|
||||
## Common Pitfalls
|
||||
|
||||
- ❌ Not testing exit codes
|
||||
- ❌ Only testing success cases
|
||||
- ❌ Hardcoding paths instead of using `path.join()`
|
||||
- ❌ Not handling async operations properly
|
||||
- ❌ Testing implementation details instead of behavior
|
||||
|
||||
## Resources
|
||||
|
||||
- [Jest Documentation](https://jestjs.io/docs/getting-started)
|
||||
- [Testing CLI Applications](https://jestjs.io/docs/cli)
|
||||
- [TypeScript with Jest](https://jestjs.io/docs/getting-started#using-typescript)
|
||||
Reference in New Issue
Block a user