or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

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

test-tools.mddocs/

0

# Test Tools and Decorators

1

2

Utilities for writing parameterized tests, BDD-style specifications, and enhanced test setup/teardown functionality. These tools extend unittest capabilities with modern testing patterns.

3

4

## Capabilities

5

6

### Parameterized Testing

7

8

Decorators for creating parameterized tests that run with multiple input values.

9

10

```python { .api }

11

def params(*paramList):

12

"""

13

Make a test function or method parameterized by parameters.

14

15

Parameters may be defined as simple values or tuples. To pass a tuple

16

as a simple value, wrap it in another tuple.

17

18

Parameters:

19

- paramList: Variable arguments, each becomes a test parameter set

20

21

Returns:

22

Decorator function that adds paramList attribute to test function

23

24

Example:

25

@params(1, 2, 3)

26

def test_nums(num):

27

assert num < 4

28

29

@params((1, 2), (2, 3), (4, 5))

30

def test_less_than(a, b):

31

assert a < b

32

"""

33

34

def cartesian_params(*paramList):

35

"""

36

Make a test function or method parameterized by cartesian product of parameters.

37

38

Parameters in the list must be defined as iterable objects (tuple or list).

39

40

Parameters:

41

- paramList: Variable arguments of iterables for cartesian product

42

43

Returns:

44

Decorator function that adds paramList attribute to test function

45

46

Example:

47

@cartesian_params((1, 2, 3), ('a', 'b'))

48

def test_nums(num, char):

49

assert num < ord(char)

50

"""

51

```

52

53

### Test Setup and Teardown Decorators

54

55

Decorators for adding setup and teardown methods to function-based tests.

56

57

```python { .api }

58

def with_setup(setup):

59

"""

60

Decorator that sets the setup method to be executed before the test.

61

62

Currently works only for function test cases.

63

64

Parameters:

65

- setup: The method to be executed before the test

66

67

Returns:

68

Decorator function that adds setup attribute to test function

69

70

Example:

71

def my_setup():

72

# setup code

73

pass

74

75

@with_setup(my_setup)

76

def test_something():

77

# test code

78

pass

79

"""

80

81

def with_teardown(teardown):

82

"""

83

Decorator that sets the teardown method to be executed after the test.

84

85

Currently works only for function test cases.

86

87

Parameters:

88

- teardown: The method to be executed after the test

89

90

Returns:

91

Decorator function that adds tearDownFunc attribute to test function

92

93

Example:

94

def my_teardown():

95

# teardown code

96

pass

97

98

@with_teardown(my_teardown)

99

def test_something():

100

# test code

101

pass

102

"""

103

```

104

105

### BDD-Style Testing (such)

106

107

Domain-specific language for writing behavior-driven tests with hierarchical organization and fixtures.

108

109

```python { .api }

110

def A(description):

111

"""

112

Test scenario context manager.

113

114

Returns a Scenario instance, conventionally bound to 'it'.

115

116

Parameters:

117

- description: Description of the test scenario

118

119

Returns:

120

Context manager yielding Scenario instance

121

122

Example:

123

with such.A('calculator functionality') as it:

124

# tests and fixtures

125

pass

126

"""

127

128

class Scenario:

129

"""

130

A test scenario that defines a set of fixtures and tests.

131

"""

132

133

def having(self, description):

134

"""

135

Define a new group under the current group.

136

137

Context manager for creating nested test groups. Fixtures and tests

138

defined within the block belong to the new group.

139

140

Parameters:

141

- description: Description of the test group

142

143

Returns:

144

Context manager yielding self

145

146

Example:

147

with it.having('valid input data'):

148

# nested tests and fixtures

149

pass

150

"""

151

152

def uses(self, layer):

153

"""

154

Add a layer as mixin to this group.

155

156

Parameters:

157

- layer: Layer class to add as mixin

158

"""

159

160

def has_setup(self, func):

161

"""

162

Add a setup method to this group.

163

164

The setup method runs once before any tests in the containing group.

165

A group may define multiple setup functions - they execute in order.

166

167

Parameters:

168

- func: Setup function to add

169

170

Returns:

171

The original function (decorator pattern)

172

173

Example:

174

@it.has_setup

175

def setup():

176

it.data = load_test_data()

177

"""

178

179

def has_teardown(self, func):

180

"""

181

Add a teardown method to this group.

182

183

The teardown method runs once after all tests in the containing group.

184

A group may define multiple teardown functions - they execute in order.

185

186

Parameters:

187

- func: Teardown function to add

188

189

Returns:

190

The original function (decorator pattern)

191

192

Example:

193

@it.has_teardown

194

def teardown():

195

cleanup_test_data()

196

"""

197

198

def has_test_setup(self, func):

199

"""

200

Add a test case setup method to this group.

201

202

The setup method runs before each test in the containing group.

203

Functions may optionally take one argument (the TestCase instance).

204

205

Parameters:

206

- func: Test setup function to add

207

208

Example:

209

@it.has_test_setup

210

def setup(case):

211

case.client = TestClient()

212

"""

213

214

def has_test_teardown(self, func):

215

"""

216

Add a test case teardown method to this group.

217

218

The teardown method runs after each test in the containing group.

219

Functions may optionally take one argument (the TestCase instance).

220

221

Parameters:

222

- func: Test teardown function to add

223

224

Example:

225

@it.has_test_teardown

226

def teardown(case):

227

case.client.close()

228

"""

229

230

def should(self, desc):

231

"""

232

Define a test case.

233

234

Each function marked with this decorator becomes a test case in the

235

current group. Test functions may optionally take one argument

236

(the TestCase instance) for executing assert methods.

237

238

Parameters:

239

- desc: Description of what the test should do, or the test function

240

if used without arguments

241

242

Returns:

243

Decorator function or Case instance if desc is a function

244

245

Example:

246

@it.should('calculate sum correctly')

247

def test_sum(case):

248

result = calculator.add(2, 3)

249

case.assertEqual(result, 5)

250

251

@it.should

252

def test_subtraction():

253

\"\"\"subtract numbers correctly\"\"\"

254

assert calculator.subtract(5, 3) == 2

255

"""

256

257

def createTests(self, mod):

258

"""

259

Generate test cases for this scenario.

260

261

You must call this with globals() to generate tests from the scenario.

262

If you don't call this, no tests will be created.

263

264

Parameters:

265

- mod: Module globals dictionary (usually globals())

266

267

Example:

268

it.createTests(globals())

269

"""

270

271

class Group:

272

"""A group of tests with common fixtures and description."""

273

274

def __init__(self, description, indent=0, parent=None, base_layer=None):

275

"""

276

Initialize test group.

277

278

Parameters:

279

- description: Group description

280

- indent: Indentation level

281

- parent: Parent group

282

- base_layer: Base layer class

283

"""

284

285

def addCase(self, case):

286

"""Add a test case to this group."""

287

288

def addSetup(self, func):

289

"""Add a setup function to this group."""

290

291

def addTeardown(self, func):

292

"""Add a teardown function to this group."""

293

294

def child(self, description, base_layer=None):

295

"""Create a child group."""

296

297

class Case:

298

"""Information about a test case."""

299

300

def __init__(self, group, func, description):

301

"""

302

Initialize test case.

303

304

Parameters:

305

- group: Parent group

306

- func: Test function

307

- description: Test description

308

"""

309

310

def __call__(self, testcase, *args):

311

"""Execute the test case with given TestCase instance."""

312

```

313

314

## Usage Examples

315

316

### Parameterized Tests

317

318

```python

319

from nose2.tools import params, cartesian_params

320

321

# Simple parameter list

322

@params(1, 2, 3, 4, 5)

323

def test_positive_numbers(num):

324

assert num > 0

325

326

# Tuple parameters

327

@params((1, 2, 3), (4, 5, 9), (6, 7, 13))

328

def test_sum(a, b, expected):

329

assert a + b == expected

330

331

# Cartesian product parameters

332

@cartesian_params([1, 2], ['a', 'b'], [True, False])

333

def test_combinations(num, char, flag):

334

# Test runs with all combinations: 8 total tests

335

assert isinstance(num, int)

336

assert isinstance(char, str)

337

assert isinstance(flag, bool)

338

339

# Class method parameterization

340

class TestMath(unittest.TestCase):

341

342

@params(0, 1, 5, 10)

343

def test_square_root(self, num):

344

result = math.sqrt(num * num)

345

self.assertEqual(result, num)

346

```

347

348

### Setup and Teardown Decorators

349

350

```python

351

from nose2.tools.decorators import with_setup, with_teardown

352

353

# Global test data

354

test_data = {}

355

356

def setup_data():

357

test_data['value'] = 42

358

test_data['list'] = [1, 2, 3]

359

360

def cleanup_data():

361

test_data.clear()

362

363

@with_setup(setup_data)

364

@with_teardown(cleanup_data)

365

def test_data_access():

366

assert test_data['value'] == 42

367

assert len(test_data['list']) == 3

368

```

369

370

### BDD-Style Testing

371

372

```python

373

from nose2.tools import such

374

375

with such.A('web application') as it:

376

377

@it.has_setup

378

def setup():

379

it.app = create_test_app()

380

it.client = it.app.test_client()

381

382

@it.has_teardown

383

def teardown():

384

it.app.cleanup()

385

386

with it.having('user authentication'):

387

388

@it.has_test_setup

389

def setup_auth(case):

390

case.user = create_test_user()

391

392

@it.should('allow login with valid credentials')

393

def test_valid_login(case):

394

response = it.client.post('/login', data={

395

'username': case.user.username,

396

'password': 'password'

397

})

398

case.assertEqual(response.status_code, 200)

399

400

@it.should('reject invalid credentials')

401

def test_invalid_login(case):

402

response = it.client.post('/login', data={

403

'username': 'wrong',

404

'password': 'wrong'

405

})

406

case.assertEqual(response.status_code, 401)

407

408

with it.having('API endpoints'):

409

410

@it.should('return JSON responses')

411

def test_json_response(case):

412

response = it.client.get('/api/status')

413

case.assertEqual(response.content_type, 'application/json')

414

415

# Generate test cases - this is required!

416

it.createTests(globals())

417

```

418

419

### Mixed Testing Styles

420

421

```python

422

import unittest

423

from nose2.tools import params, such

424

from nose2.tools.decorators import with_setup

425

426

# Traditional unittest class

427

class TestBasicMath(unittest.TestCase):

428

429

@params(1, 2, 3, 5, 8)

430

def test_fibonacci_numbers(self, num):

431

self.assertIn(num, [1, 1, 2, 3, 5, 8, 13, 21])

432

433

# Function-based tests with setup

434

def setup_calculator():

435

global calc

436

calc = Calculator()

437

438

@with_setup(setup_calculator)

439

@params((2, 3, 5), (10, 5, 15), (0, 100, 100))

440

def test_addition(a, b, expected):

441

result = calc.add(a, b)

442

assert result == expected

443

444

# BDD-style specification

445

with such.A('calculator application') as it:

446

447

@it.has_setup

448

def setup():

449

it.calc = Calculator()

450

451

with it.having('basic arithmetic operations'):

452

453

@it.should('add positive numbers')

454

def test_positive_addition(case):

455

result = it.calc.add(2, 3)

456

case.assertEqual(result, 5)

457

458

@it.should('handle zero values')

459

def test_zero_addition(case):

460

case.assertEqual(it.calc.add(0, 5), 5)

461

case.assertEqual(it.calc.add(5, 0), 5)

462

463

it.createTests(globals())

464

```