or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

configuration.mdindex.mdplugin-development.mdtest-discovery.mdtest-tools.md

configuration.mddocs/

0

# Configuration Management

1

2

System for managing test configuration through command-line arguments, configuration files, and programmatic settings. Provides flexible configuration options for test discovery, plugin behavior, and execution parameters.

3

4

## Capabilities

5

6

### Configuration Class

7

8

The Config class provides type-safe access to configuration values with default handling.

9

10

```python { .api }

11

class Config:

12

"""

13

Configuration for a plugin or other entities.

14

15

Encapsulates configuration for a single plugin or element.

16

Corresponds to a ConfigParser section but provides extended

17

interface for extracting items as specific types.

18

"""

19

20

def __init__(self, items):

21

"""

22

Initialize configuration with key-value pairs.

23

24

Parameters:

25

- items: List of (key, value) tuples from config section

26

"""

27

28

def __getitem__(self, key):

29

"""

30

Get raw configuration values for key.

31

32

Parameters:

33

- key: Configuration key name

34

35

Returns:

36

List of values for the key

37

"""

38

39

def as_bool(self, key, default=None):

40

"""

41

Get key value as boolean.

42

43

1, t, true, on, yes, y (case insensitive) are accepted as True.

44

All other values are False. Empty setting (key=) returns False.

45

46

Parameters:

47

- key: Configuration key name

48

- default: Default value if key not found

49

50

Returns:

51

Boolean value or default

52

"""

53

54

def as_int(self, key, default=None):

55

"""

56

Get key value as integer.

57

58

Parameters:

59

- key: Configuration key name

60

- default: Default value if key not found

61

62

Returns:

63

Integer value or default

64

"""

65

66

def as_float(self, key, default=None):

67

"""

68

Get key value as float.

69

70

Parameters:

71

- key: Configuration key name

72

- default: Default value if key not found

73

74

Returns:

75

Float value or default

76

"""

77

78

def as_str(self, key, default=None):

79

"""

80

Get key value as string.

81

82

Parameters:

83

- key: Configuration key name

84

- default: Default value if key not found

85

86

Returns:

87

String value or default

88

"""

89

90

def as_list(self, key, default=None):

91

"""

92

Get key value as list.

93

94

The value is split into lines and returned as a list. Lines

95

are stripped of whitespace, and lines beginning with # are

96

skipped.

97

98

Parameters:

99

- key: Configuration key name

100

- default: Default value if key not found

101

102

Returns:

103

List of string values or default

104

"""

105

106

def get(self, key, default=None):

107

"""

108

Get configuration value with default.

109

110

Alias for as_str() method for compatibility.

111

112

Parameters:

113

- key: Configuration key name

114

- default: Default value if key not found

115

116

Returns:

117

String value or default

118

"""

119

120

def items(self):

121

"""

122

Get all configuration items.

123

124

Returns:

125

List of (key, value) tuples

126

"""

127

128

def has_option(self, key):

129

"""

130

Check if configuration key exists.

131

132

Parameters:

133

- key: Configuration key name

134

135

Returns:

136

True if key exists, False otherwise

137

"""

138

```

139

140

### Command Line Arguments

141

142

Core command-line arguments supported by nose2.

143

144

```python { .api }

145

# Test Discovery Arguments

146

def add_discovery_arguments(parser):

147

"""Add test discovery arguments to parser."""

148

parser.add_argument('-s', '--start-dir',

149

help='Directory to start discovery')

150

parser.add_argument('-t', '--top-level-directory',

151

help='Top level directory of project')

152

parser.add_argument('-p', '--pattern',

153

help='Pattern to match test files')

154

155

# Plugin Arguments

156

def add_plugin_arguments(parser):

157

"""Add plugin-related arguments to parser."""

158

parser.add_argument('--plugin',

159

help='Load this plugin module')

160

parser.add_argument('--exclude-plugin',

161

help='Do not load this plugin module')

162

parser.add_argument('--no-plugins',

163

help='Do not load any plugins')

164

165

# Configuration Arguments

166

def add_config_arguments(parser):

167

"""Add configuration file arguments to parser."""

168

parser.add_argument('-c', '--config',

169

help='Config files to load')

170

parser.add_argument('--no-user-config',

171

help='Do not load user config files')

172

173

# Output Arguments

174

def add_output_arguments(parser):

175

"""Add output control arguments to parser."""

176

parser.add_argument('-v', '--verbose',

177

help='Increase verbosity')

178

parser.add_argument('-q', '--quiet',

179

help='Decrease verbosity')

180

parser.add_argument('--log-level',

181

help='Set logging level')

182

```

183

184

## Configuration Files

185

186

### Configuration File Locations

187

188

nose2 searches for configuration files in the following order:

189

190

1. Files specified with `--config` or `-c`

191

2. Project files: `unittest.cfg`, `nose2.cfg`, `pyproject.toml` (in start directory)

192

3. User files: `~/.unittest.cfg`, `~/.nose2.cfg` (if `--no-user-config` not used)

193

194

### Configuration File Format

195

196

```ini

197

# unittest.cfg or nose2.cfg

198

[unittest]

199

# Core nose2 settings

200

start-dir = tests

201

top-level-directory = .

202

pattern = test_*.py

203

test-method-prefix = test

204

plugins = nose2.plugins.coverage

205

nose2.plugins.junitxml

206

my_custom_plugin

207

208

# Plugin-specific sections

209

[coverage]

210

always-on = True

211

coverage = mypackage

212

coverage-report = html

213

coverage-config = .coveragerc

214

215

[junitxml]

216

always-on = False

217

path = test-results.xml

218

219

[my-custom-plugin]

220

enabled = true

221

threshold = 1.0

222

output-file = results.txt

223

```

224

225

### TOML Configuration (pyproject.toml)

226

227

```toml

228

[tool.nose2]

229

start-dir = "tests"

230

top-level-directory = "."

231

pattern = "test_*.py"

232

plugins = [

233

"nose2.plugins.coverage",

234

"nose2.plugins.junitxml"

235

]

236

237

[tool.nose2.coverage]

238

always-on = true

239

coverage = ["mypackage"]

240

coverage-report = "html"

241

242

[tool.nose2.junitxml]

243

path = "test-results.xml"

244

```

245

246

## Usage Examples

247

248

### Programmatic Configuration

249

250

```python

251

from nose2.session import Session

252

from nose2.config import Config

253

254

# Create and configure session

255

session = Session()

256

257

# Load configuration files

258

session.loadConfigFiles('unittest.cfg', 'nose2.cfg')

259

260

# Get plugin configuration

261

coverage_config = session.get('coverage')

262

enabled = coverage_config.as_bool('always-on', default=False)

263

report_type = coverage_config.as_str('coverage-report', default='term')

264

packages = coverage_config.as_list('coverage', default=[])

265

266

print(f"Coverage enabled: {enabled}")

267

print(f"Report type: {report_type}")

268

print(f"Packages: {packages}")

269

```

270

271

### Plugin Configuration

272

273

```python

274

from nose2.events import Plugin

275

276

class MyPlugin(Plugin):

277

configSection = 'my-plugin'

278

279

def __init__(self):

280

# Extract all config values in __init__ for sphinx docs

281

self.enabled = self.config.as_bool('enabled', default=True)

282

self.threshold = self.config.as_float('threshold', default=1.0)

283

self.output_file = self.config.as_str('output-file', default='output.txt')

284

self.patterns = self.config.as_list('patterns', default=['*.py'])

285

self.debug = self.config.as_bool('debug', default=False)

286

287

# Check if required config is present

288

if not self.config.has_option('required-setting'):

289

raise ValueError("required-setting must be specified")

290

291

def some_method(self):

292

if self.enabled:

293

# Use configuration values

294

with open(self.output_file, 'w') as f:

295

f.write(f"Threshold: {self.threshold}\n")

296

f.write(f"Patterns: {self.patterns}\n")

297

```

298

299

### Command Line Usage

300

301

```bash

302

# Basic test discovery

303

nose2

304

305

# Custom start directory and pattern

306

nose2 --start-dir tests --pattern 'spec_*.py'

307

308

# Load specific plugins

309

nose2 --plugin nose2.plugins.coverage --plugin my_plugin

310

311

# Exclude plugins

312

nose2 --exclude-plugin nose2.plugins.buffer

313

314

# Custom configuration file

315

nose2 --config my_test_config.cfg

316

317

# Verbosity control

318

nose2 -v -v # Very verbose

319

nose2 -q # Quiet

320

321

# Logging level

322

nose2 --log-level DEBUG

323

324

# No user config

325

nose2 --no-user-config

326

327

# No plugins at all

328

nose2 --no-plugins

329

```

330

331

### Complex Configuration Example

332

333

```ini

334

# nose2.cfg - Complete configuration example

335

[unittest]

336

# Test discovery

337

start-dir = tests

338

top-level-directory = .

339

pattern = test_*.py

340

test-method-prefix = test

341

342

# Plugin loading

343

plugins = nose2.plugins.coverage

344

nose2.plugins.junitxml

345

nose2.plugins.mp

346

nose2.plugins.logcapture

347

custom_plugins.timing

348

custom_plugins.database

349

350

# Exclude problematic plugins

351

exclude-plugins = nose2.plugins.debugger

352

353

# Verbosity and logging

354

verbosity = 2

355

log-level = INFO

356

357

[coverage]

358

always-on = true

359

coverage = mypackage

360

mypackage.submodule

361

coverage-report = html

362

coverage-config = .coveragerc

363

fail-under = 80

364

365

[junitxml]

366

path = test-results/junit.xml

367

test-properties = name value

368

version 1.0

369

370

[mp]

371

processes = 4

372

test-timeout = 30

373

374

[logcapture]

375

always-on = true

376

clear-handlers = true

377

filter = -nose2

378

log-level = DEBUG

379

380

[timing]

381

enabled = true

382

threshold = 0.5

383

output-file = slow-tests.txt

384

385

[database]

386

url = postgresql://localhost/test_db

387

reset = true

388

fixtures = fixtures/users.json

389

fixtures/products.json

390

```

391

392

### Environment Variable Configuration

393

394

```python

395

import os

396

from nose2.main import PluggableTestProgram

397

398

# Override config with environment variables

399

start_dir = os.environ.get('NOSE2_START_DIR', 'tests')

400

verbosity = int(os.environ.get('NOSE2_VERBOSITY', '1'))

401

plugins = os.environ.get('NOSE2_PLUGINS', '').split(',') if os.environ.get('NOSE2_PLUGINS') else []

402

403

# Run with environment-based configuration

404

PluggableTestProgram(

405

argv=['nose2', '--start-dir', start_dir, f'--verbosity={verbosity}'] +

406

[f'--plugin={p}' for p in plugins if p.strip()]

407

)

408

```

409

410

### Configuration Validation

411

412

```python

413

from nose2.events import Plugin

414

415

class ValidatedPlugin(Plugin):

416

configSection = 'validated-plugin'

417

418

def __init__(self):

419

# Validate required settings

420

required_keys = ['api_key', 'endpoint']

421

for key in required_keys:

422

if not self.config.has_option(key):

423

raise ValueError(f"Missing required configuration: {key}")

424

425

# Extract and validate settings

426

self.api_key = self.config.as_str('api_key')

427

self.endpoint = self.config.as_str('endpoint')

428

self.timeout = self.config.as_int('timeout', default=30)

429

self.retries = self.config.as_int('retries', default=3)

430

431

# Validate ranges

432

if self.timeout < 1 or self.timeout > 300:

433

raise ValueError("timeout must be between 1 and 300 seconds")

434

435

if self.retries < 0 or self.retries > 10:

436

raise ValueError("retries must be between 0 and 10")

437

438

# Validate formats

439

if not self.endpoint.startswith('http'):

440

raise ValueError("endpoint must be a valid HTTP URL")

441

```