or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

argument-processing.mdcommand-system.mdcore-driver.mdcustom-commands.mderror-handling.mdhelp-system.mdindex.mdoutput-formatting.mdplugin-system.mdtesting-framework.mdutilities.md

testing-framework.mddocs/

0

# Testing Framework

1

2

Extensive testing utilities for developing and validating AWS CLI commands, including test base classes, output capture, and AWS service mocking. This framework enables comprehensive testing of CLI functionality and custom extensions.

3

4

## Capabilities

5

6

### Base Test Classes

7

8

Foundation test classes providing common CLI testing functionality and AWS service mocking.

9

10

```python { .api }

11

class BaseCLIDriverTest(unittest.TestCase):

12

"""

13

Base test class for CLI driver testing.

14

Provides CLI driver setup and common testing utilities.

15

"""

16

17

def setUp(self): ...

18

def create_clidriver(self): ...

19

def run_cmd(self, cmdline, expected_rc=0): ...

20

def mock_aws_service(self, service_name): ...

21

def register_command(self, command_name, command_class): ...

22

def temporary_file(self): ...

23

def environment_patch(self, env_vars): ...

24

25

class BaseAWSCommandParamsTest(unittest.TestCase):

26

"""

27

Base test class for AWS command parameter testing.

28

Provides AWS service mocking and parameter validation.

29

"""

30

31

def setUp(self): ...

32

def run_cmd(self, cmdline): ...

33

def assert_params_for_cmd(self, expected_params, ignore_params=None): ...

34

def before_parameter_build(self, params, model): ...

35

def after_parameter_build(self, params): ...

36

37

class BaseS3CLICommand(unittest.TestCase):

38

"""

39

Base test class for S3 command testing.

40

Provides S3-specific testing utilities and mocking.

41

"""

42

43

def setUp(self): ...

44

def create_bucket(self, bucket_name): ...

45

def put_object(self, bucket_name, key, content): ...

46

def list_objects(self, bucket_name): ...

47

def delete_object(self, bucket_name, key): ...

48

def random_bucket_name(self): ...

49

50

class BaseAWSHelpOutputTest(unittest.TestCase):

51

"""

52

Base test class for AWS help output testing.

53

Provides help command testing utilities and output validation.

54

"""

55

56

def setUp(self): ...

57

def run_help_cmd(self, cmdline): ...

58

def assert_contains(self, contains): ...

59

def assert_not_contains(self, not_contains): ...

60

def assert_text_order(self, *args): ...

61

def get_help_contents(self): ...

62

63

class BaseCLIWireResponseTest(unittest.TestCase):

64

"""

65

Base test class for CLI wire response testing.

66

Provides HTTP response mocking and wire-level testing utilities.

67

"""

68

69

def setUp(self): ...

70

def create_http_response(self, status_code, headers, content): ...

71

def setup_http_adapter(self): ...

72

def add_response(self, method, url, **kwargs): ...

73

def assert_request_made(self, method, url): ...

74

```

75

76

### CLI Testing Functions

77

78

Core functions for executing and testing CLI commands programmatically.

79

80

```python { .api }

81

def create_clidriver():

82

"""

83

Create CLI driver instance for testing.

84

85

Returns:

86

CLIDriver: Configured driver for test execution

87

"""

88

89

def aws(*args):

90

"""

91

Execute AWS CLI commands in test environment.

92

93

Parameters:

94

*args: CLI command arguments

95

96

Returns:

97

Test execution result with stdout, stderr, and exit code

98

"""

99

100

def capture_output():

101

"""

102

Capture CLI output for testing and validation.

103

104

Returns:

105

Context manager for output capture

106

"""

107

```

108

109

### File and Resource Management

110

111

Utilities for creating and managing temporary files and resources during testing, including platform-specific testing decorators.

112

113

```python { .api }

114

class FileCreator:

115

"""

116

Helper class for creating test files and directories.

117

Provides automatic cleanup and resource management.

118

"""

119

120

def temporary_file(mode='w', suffix='', prefix='tmp', dir=None, delete=True):

121

"""

122

Create temporary file for testing.

123

124

Parameters:

125

mode (str): File mode (default: 'w')

126

suffix (str): File suffix (default: '')

127

prefix (str): File prefix (default: 'tmp')

128

dir (str): Directory for temporary file (default: None)

129

delete (bool): Delete file on exit (default: True)

130

131

Returns:

132

Context manager providing temporary file path

133

"""

134

135

def skip_if_windows(reason='Test not supported on Windows'):

136

"""

137

Skip test if running on Windows platform.

138

139

Parameters:

140

reason (str): Reason for skipping test

141

142

Returns:

143

unittest.skip decorator

144

"""

145

146

def skip_if_not_windows(reason='Test only supported on Windows'):

147

"""

148

Skip test if not running on Windows platform.

149

150

Parameters:

151

reason (str): Reason for skipping test

152

153

Returns:

154

unittest.skip decorator

155

"""

156

157

def random_bucket_name(prefix='test-bucket', suffix_length=8):

158

"""

159

Generate random S3 bucket name for testing.

160

161

Parameters:

162

prefix (str): Bucket name prefix (default: 'test-bucket')

163

suffix_length (int): Length of random suffix (default: 8)

164

165

Returns:

166

str: Random bucket name following S3 naming conventions

167

"""

168

169

def create_bucket_notification_event(bucket_name, key, event_name='s3:ObjectCreated:*'):

170

"""

171

Create S3 bucket notification event for testing.

172

173

Parameters:

174

bucket_name (str): S3 bucket name

175

key (str): Object key

176

event_name (str): Event type (default: 's3:ObjectCreated:*')

177

178

Returns:

179

dict: S3 event notification structure

180

"""

181

182

def create_multipart_upload_id(bucket_name, key):

183

"""

184

Create multipart upload ID for S3 testing.

185

186

Parameters:

187

bucket_name (str): S3 bucket name

188

key (str): Object key

189

190

Returns:

191

str: Multipart upload ID

192

"""

193

```

194

195

### HTTP Response Testing

196

197

Utilities for testing HTTP interactions and wire-level protocol behavior.

198

199

```python { .api }

200

def mock_make_request(status_code=200, headers=None, content=b''):

201

"""

202

Create mock HTTP request for testing.

203

204

Parameters:

205

status_code (int): HTTP status code (default: 200)

206

headers (dict): Response headers (default: None)

207

content (bytes): Response content (default: b'')

208

209

Returns:

210

Mock HTTP response object

211

"""

212

213

def assert_request_headers(request, expected_headers):

214

"""

215

Assert that request contains expected headers.

216

217

Parameters:

218

request: HTTP request object

219

expected_headers (dict): Expected headers to validate

220

"""

221

222

def create_streaming_response(chunks, status_code=200):

223

"""

224

Create streaming HTTP response for testing.

225

226

Parameters:

227

chunks (list): List of content chunks

228

status_code (int): HTTP status code (default: 200)

229

230

Returns:

231

Streaming response object

232

"""

233

```

234

235

### Platform Testing Utilities

236

237

Cross-platform testing utilities and decorators for handling OS-specific behavior.

238

239

```python { .api }

240

def platform_test(platforms):

241

"""

242

Decorator to run test only on specified platforms.

243

244

Parameters:

245

platforms (list): List of platform names ('windows', 'darwin', 'linux')

246

247

Returns:

248

Test decorator

249

"""

250

251

def requires_binary(binary_name):

252

"""

253

Skip test if required binary is not available.

254

255

Parameters:

256

binary_name (str): Name of required binary

257

258

Returns:

259

unittest.skipUnless decorator

260

"""

261

262

def mock_platform(platform_name):

263

"""

264

Mock platform.system() for testing.

265

266

Parameters:

267

platform_name (str): Platform name to mock

268

269

Returns:

270

Context manager for platform mocking

271

"""

272

```

273

274

**Usage Example:**

275

```python

276

import unittest

277

from awscli.testutils import (

278

BaseCLIDriverTest, BaseAWSHelpOutputTest, BaseCLIWireResponseTest,

279

capture_output, temporary_file, skip_if_windows, platform_test

280

)

281

282

class TestCustomCommand(BaseCLIDriverTest):

283

def test_custom_command_execution(self):

284

"""Test custom command executes successfully."""

285

with capture_output() as captured:

286

exit_code = self.run_cmd(['custom-command', '--help'])

287

288

self.assertEqual(exit_code, 0)

289

self.assertIn('Custom command help', captured.stdout)

290

291

def test_custom_command_with_args(self):

292

"""Test custom command with arguments."""

293

result = self.run_cmd([

294

'custom-command',

295

'--input', 'test-input',

296

'--format', 'json'

297

])

298

299

self.assertEqual(result.rc, 0)

300

self.assertIn('test-input', result.stdout)

301

302

class TestHelpOutput(BaseAWSHelpOutputTest):

303

def test_service_help_output(self):

304

"""Test service help output format."""

305

self.run_help_cmd(['ec2', 'help'])

306

307

# Validate help content

308

self.assert_contains('EC2')

309

self.assert_contains('describe-instances')

310

self.assert_text_order('DESCRIPTION', 'SYNOPSIS', 'OPTIONS')

311

312

def test_operation_help_output(self):

313

"""Test operation help output."""

314

self.run_help_cmd(['ec2', 'describe-instances', 'help'])

315

316

self.assert_contains('describe-instances')

317

self.assert_not_contains('invalid-content')

318

319

class TestWireResponse(BaseCLIWireResponseTest):

320

def test_http_response_handling(self):

321

"""Test HTTP response processing."""

322

# Set up mock response

323

response = self.create_http_response(

324

status_code=200,

325

headers={'Content-Type': 'application/json'},

326

content=b'{"instances": []}'

327

)

328

329

self.add_response('POST', 'https://ec2.amazonaws.com/', response)

330

331

# Execute command

332

result = self.run_cmd(['ec2', 'describe-instances'])

333

334

# Verify request was made

335

self.assert_request_made('POST', 'https://ec2.amazonaws.com/')

336

self.assertEqual(result.rc, 0)

337

```

338

339

## Test Class Implementations

340

341

### CLI Driver Testing

342

343

```python

344

class TestCLIDriver(BaseCLIDriverTest):

345

"""Test CLI driver functionality."""

346

347

def setUp(self):

348

super().setUp()

349

# Additional setup for CLI driver testing

350

self.driver = self.create_clidriver()

351

352

def test_service_command_execution(self):

353

"""Test service command execution."""

354

# Mock AWS service

355

with self.mock_aws_service('ec2'):

356

result = self.run_cmd(['ec2', 'describe-instances'])

357

self.assertEqual(result.rc, 0)

358

359

def test_argument_parsing(self):

360

"""Test argument parsing functionality."""

361

result = self.run_cmd([

362

's3', 'ls', 's3://my-bucket',

363

'--recursive',

364

'--output', 'json'

365

])

366

367

self.assertEqual(result.rc, 0)

368

self.validate_json_output(result.stdout)

369

```

370

371

### AWS Command Parameter Testing

372

373

```python

374

class TestEC2Commands(BaseAWSCommandParamsTest):

375

"""Test EC2 command parameter handling."""

376

377

def setUp(self):

378

super().setUp()

379

self.service_name = 'ec2'

380

381

def test_describe_instances_parameters(self):

382

"""Test describe-instances parameter processing."""

383

# Set up expected parameters

384

expected_params = {

385

'InstanceIds': ['i-1234567890abcdef0'],

386

'Filters': [

387

{'Name': 'instance-state-name', 'Values': ['running']}

388

]

389

}

390

391

# Execute command and verify parameters

392

self.run_cmd([

393

'ec2', 'describe-instances',

394

'--instance-ids', 'i-1234567890abcdef0',

395

'--filters', 'Name=instance-state-name,Values=running'

396

])

397

398

# Verify parameters were processed correctly

399

self.assert_params_for_cmd(expected_params)

400

401

def test_parameter_validation(self):

402

"""Test parameter validation and error handling."""

403

result = self.run_cmd([

404

'ec2', 'describe-instances',

405

'--invalid-parameter', 'value'

406

])

407

408

self.assertNotEqual(result.rc, 0)

409

self.assertIn('Unknown parameter', result.stderr)

410

```

411

412

### S3 Command Testing

413

414

```python

415

class TestS3Commands(BaseS3CLICommand):

416

"""Test S3-specific command functionality."""

417

418

def setUp(self):

419

super().setUp()

420

self.bucket_name = self.random_bucket_name()

421

self.create_bucket(self.bucket_name)

422

423

def test_s3_ls_command(self):

424

"""Test S3 ls command."""

425

# Create test objects

426

self.put_object(self.bucket_name, 'test-file.txt', b'test content')

427

428

# Execute ls command

429

result = self.run_cmd(['s3', 'ls', f's3://{self.bucket_name}'])

430

431

self.assertEqual(result.rc, 0)

432

self.assertIn('test-file.txt', result.stdout)

433

434

def test_s3_cp_command(self):

435

"""Test S3 cp command."""

436

with self.temporary_file() as temp_file:

437

# Write test content

438

with open(temp_file, 'w') as f:

439

f.write('test content')

440

441

# Copy to S3

442

result = self.run_cmd([

443

's3', 'cp', temp_file, f's3://{self.bucket_name}/uploaded.txt'

444

])

445

446

self.assertEqual(result.rc, 0)

447

448

# Verify object exists

449

objects = self.list_objects(self.bucket_name)

450

self.assertIn('uploaded.txt', [obj['Key'] for obj in objects])

451

```

452

453

## Advanced Testing Patterns

454

455

### Mock Service Integration

456

457

```python

458

class TestWithMockServices(BaseCLIDriverTest):

459

"""Test with comprehensive service mocking."""

460

461

def setUp(self):

462

super().setUp()

463

# Set up service mocks

464

self.ec2_mock = self.mock_aws_service('ec2')

465

self.s3_mock = self.mock_aws_service('s3')

466

467

def test_cross_service_operation(self):

468

"""Test operation involving multiple services."""

469

# Configure EC2 mock

470

self.ec2_mock.describe_instances.return_value = {

471

'Reservations': [{

472

'Instances': [{

473

'InstanceId': 'i-1234567890abcdef0',

474

'State': {'Name': 'running'}

475

}]

476

}]

477

}

478

479

# Configure S3 mock

480

self.s3_mock.list_objects_v2.return_value = {

481

'Contents': [{'Key': 'config.json', 'Size': 1024}]

482

}

483

484

# Execute commands and verify interactions

485

ec2_result = self.run_cmd(['ec2', 'describe-instances'])

486

s3_result = self.run_cmd(['s3', 'ls', 's3://config-bucket'])

487

488

self.assertEqual(ec2_result.rc, 0)

489

self.assertEqual(s3_result.rc, 0)

490

491

# Verify service calls

492

self.ec2_mock.describe_instances.assert_called_once()

493

self.s3_mock.list_objects_v2.assert_called_once()

494

```

495

496

### Custom Command Testing

497

498

```python

499

class TestCustomCommands(BaseCLIDriverTest):

500

"""Test custom command implementations."""

501

502

def setUp(self):

503

super().setUp()

504

# Register custom commands

505

from mypackage.commands import MyCustomCommand

506

self.register_command('my-custom', MyCustomCommand)

507

508

def test_custom_command_registration(self):

509

"""Test custom command is properly registered."""

510

result = self.run_cmd(['help'])

511

self.assertIn('my-custom', result.stdout)

512

513

def test_custom_command_execution(self):

514

"""Test custom command execution."""

515

result = self.run_cmd([

516

'my-custom',

517

'--parameter', 'value',

518

'--flag'

519

])

520

521

self.assertEqual(result.rc, 0)

522

self.assertIn('Expected output', result.stdout)

523

524

def test_custom_command_error_handling(self):

525

"""Test custom command error handling."""

526

result = self.run_cmd([

527

'my-custom',

528

'--invalid-parameter', 'value'

529

])

530

531

self.assertNotEqual(result.rc, 0)

532

self.assertIn('Invalid parameter', result.stderr)

533

```

534

535

### Output Format Testing

536

537

```python

538

class TestOutputFormats(BaseCLIDriverTest):

539

"""Test different output format handling."""

540

541

def test_json_output(self):

542

"""Test JSON output format."""

543

result = self.run_cmd([

544

'ec2', 'describe-instances',

545

'--output', 'json'

546

])

547

548

self.assertEqual(result.rc, 0)

549

550

# Validate JSON structure

551

import json

552

data = json.loads(result.stdout)

553

self.assertIn('Reservations', data)

554

555

def test_table_output(self):

556

"""Test table output format."""

557

result = self.run_cmd([

558

'ec2', 'describe-instances',

559

'--output', 'table'

560

])

561

562

self.assertEqual(result.rc, 0)

563

564

# Validate table structure

565

lines = result.stdout.strip().split('\n')

566

self.assertGreater(len(lines), 2) # Header + data

567

self.assertIn('|', result.stdout) # Table formatting

568

569

def test_text_output(self):

570

"""Test text output format."""

571

result = self.run_cmd([

572

'ec2', 'describe-instances',

573

'--output', 'text'

574

])

575

576

self.assertEqual(result.rc, 0)

577

self.assertIn('RESERVATIONS', result.stdout)

578

```

579

580

## Test Utilities and Helpers

581

582

### File Management

583

584

```python

585

class TestFileOperations(BaseCLIDriverTest):

586

"""Test file-based operations."""

587

588

def test_with_temporary_files(self):

589

"""Test operations with temporary files."""

590

with self.temporary_file() as temp_path:

591

# Write test data

592

with open(temp_path, 'w') as f:

593

f.write('{"test": "data"}')

594

595

# Use file in command

596

result = self.run_cmd([

597

'ec2', 'run-instances',

598

'--cli-input-json', f'file://{temp_path}'

599

])

600

601

# File is automatically cleaned up

602

```

603

604

### Environment Configuration

605

606

```python

607

def test_with_environment_config(self):

608

"""Test with specific environment configuration."""

609

import os

610

611

# Set test environment

612

test_env = {

613

'AWS_DEFAULT_REGION': 'us-west-2',

614

'AWS_DEFAULT_OUTPUT': 'json'

615

}

616

617

with self.environment_patch(test_env):

618

result = self.run_cmd(['configure', 'list'])

619

self.assertIn('us-west-2', result.stdout)

620

self.assertIn('json', result.stdout)

621

622

### Platform-Specific Testing

623

624

```python

625

class TestPlatformBehavior(BaseCLIDriverTest):

626

"""Test platform-specific command behavior."""

627

628

@skip_if_windows('Unix-specific path handling')

629

def test_unix_path_handling(self):

630

"""Test Unix path handling."""

631

result = self.run_cmd([

632

's3', 'cp', '/tmp/test.txt', 's3://bucket/test.txt'

633

])

634

self.assertEqual(result.rc, 0)

635

636

@skip_if_not_windows('Windows-specific drive handling')

637

def test_windows_drive_handling(self):

638

"""Test Windows drive handling."""

639

result = self.run_cmd([

640

's3', 'cp', 'C:\\temp\\test.txt', 's3://bucket/test.txt'

641

])

642

self.assertEqual(result.rc, 0)

643

644

@platform_test(['linux', 'darwin'])

645

def test_unix_only_feature(self):

646

"""Test feature available only on Unix systems."""

647

result = self.run_cmd(['configure', 'set', 'cli_pager', 'less'])

648

self.assertEqual(result.rc, 0)

649

650

@requires_binary('git')

651

def test_git_integration(self):

652

"""Test Git integration functionality."""

653

with temporary_file(suffix='.git') as git_file:

654

result = self.run_cmd(['codecommit', 'credential-helper', git_file])

655

self.assertEqual(result.rc, 0)

656

657

### HTTP Wire Testing

658

659

```python

660

class TestHTTPProtocol(BaseCLIWireResponseTest):

661

"""Test HTTP protocol interactions."""

662

663

def setUp(self):

664

super().setUp()

665

self.setup_http_adapter()

666

667

def test_retry_behavior(self):

668

"""Test retry behavior on HTTP errors."""

669

# First call fails with 500

670

self.add_response(

671

'POST', 'https://ec2.amazonaws.com/',

672

status_code=500,

673

content=b'Internal Server Error'

674

)

675

676

# Second call succeeds

677

self.add_response(

678

'POST', 'https://ec2.amazonaws.com/',

679

status_code=200,

680

content=b'{"Reservations": []}'

681

)

682

683

result = self.run_cmd(['ec2', 'describe-instances'])

684

685

# Should succeed after retry

686

self.assertEqual(result.rc, 0)

687

self.assert_request_made('POST', 'https://ec2.amazonaws.com/')

688

689

def test_streaming_response(self):

690

"""Test streaming response handling."""

691

chunks = [b'{"', b'Reservations', b'": []}']

692

response = self.create_streaming_response(chunks)

693

694

self.add_response('POST', 'https://ec2.amazonaws.com/', response)

695

696

result = self.run_cmd(['ec2', 'describe-instances'])

697

self.assertEqual(result.rc, 0)

698

699

# Verify complete JSON was processed

700

import json

701

data = json.loads(result.stdout)

702

self.assertIn('Reservations', data)

703

```