or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

builtin-libraries.mdconfiguration-variables.mdcore-execution.mdindex.mdlibrary-development.mdparsing-model.md

library-development.mddocs/

0

# Library Development

1

2

Robot Framework provides comprehensive APIs for creating custom test libraries. Libraries can implement keywords using static, dynamic, or hybrid approaches, with full support for type conversion, argument handling, and test execution integration.

3

4

## Capabilities

5

6

### Library Decorators

7

8

Decorators for controlling keyword discovery and library behavior.

9

10

```python { .api }

11

@keyword(name=None, tags=(), types=()):

12

"""

13

Decorator to set custom name, tags, and argument types for keywords.

14

15

Args:

16

name: Custom keyword name (defaults to function name)

17

tags: Sequence of tags to assign to the keyword

18

types: Type hints for argument conversion

19

"""

20

21

@library(scope=None, version=None, converters=None, doc_format=None,

22

listener=None, auto_keywords=False):

23

"""

24

Decorator to control library-wide settings and keyword discovery.

25

26

Args:

27

scope: Library scope ("GLOBAL", "SUITE", "TEST", "TASK")

28

version: Library version

29

converters: Custom type converters dictionary

30

doc_format: Documentation format ("ROBOT", "HTML", "TEXT", "REST")

31

listener: Listener instance for library events

32

auto_keywords: Whether to automatically discover keywords

33

"""

34

35

@not_keyword:

36

"""

37

Decorator to prevent functions from being exposed as keywords.

38

"""

39

```

40

41

**Usage Examples:**

42

43

```python

44

from robot.api.deco import keyword, library, not_keyword

45

46

@library(scope='SUITE', version='1.0')

47

class MyLibrary:

48

49

@keyword('Custom Keyword Name', tags=['web', 'ui'])

50

def original_function_name(self, text, timeout=5):

51

"""Keyword with custom name and tags."""

52

pass

53

54

@keyword(types=[int, float])

55

def calculate_sum(self, num1, num2):

56

"""Keyword with type conversion."""

57

return num1 + num2

58

59

@not_keyword

60

def helper_method(self):

61

"""This method won't be exposed as a keyword."""

62

pass

63

64

def regular_keyword(self, arg1, arg2='default'):

65

"""Regular keyword discovered automatically."""

66

pass

67

```

68

69

### Exception Classes

70

71

Specialized exceptions for controlling test execution flow and reporting.

72

73

```python { .api }

74

class Failure(AssertionError):

75

"""

76

Report failed validation with HTML support.

77

78

Args:

79

message: Exception message

80

html: When True, message is treated as HTML

81

"""

82

def __init__(self, message: str, html: bool = False): ...

83

84

class ContinuableFailure(Failure):

85

"""

86

Report failed validation but allow test execution to continue.

87

"""

88

89

class Error(RuntimeError):

90

"""

91

Report error in execution (incorrect keyword usage).

92

93

Args:

94

message: Exception message

95

html: When True, message is treated as HTML

96

"""

97

def __init__(self, message: str, html: bool = False): ...

98

99

class FatalError(Error):

100

"""

101

Report error that stops the whole test execution.

102

"""

103

104

class SkipExecution(Exception):

105

"""

106

Mark the executed test or task as skipped.

107

108

Args:

109

message: Skip reason message

110

html: When True, message is treated as HTML

111

"""

112

def __init__(self, message: str, html: bool = False): ...

113

```

114

115

**Usage Examples:**

116

117

```python

118

from robot.api import Failure, ContinuableFailure, Error, SkipExecution

119

120

class ValidationLibrary:

121

122

def validate_user_input(self, input_data):

123

if not input_data:

124

raise Failure("Input data cannot be empty")

125

126

if len(input_data) < 3:

127

raise ContinuableFailure(

128

"Input too short, continuing with <em>warning</em>",

129

html=True

130

)

131

132

def connect_to_service(self, url):

133

if not url.startswith(('http://', 'https://')):

134

raise Error("Invalid URL format provided")

135

136

def run_critical_test(self):

137

try:

138

self.perform_critical_operation()

139

except CriticalSystemError:

140

raise FatalError("Critical system failure - stopping all tests")

141

142

def check_environment(self):

143

if not self.is_test_environment_ready():

144

raise SkipExecution("Test environment not ready, skipping test")

145

```

146

147

### Logging API

148

149

Programmatic logging interface for test libraries.

150

151

```python { .api }

152

def write(msg: str, level: str = "INFO", html: bool = False):

153

"""Write message to log file using specified level."""

154

155

def trace(msg: str, html: bool = False):

156

"""Write TRACE level message (not logged by default)."""

157

158

def debug(msg: str, html: bool = False):

159

"""Write DEBUG level message (not logged by default)."""

160

161

def info(msg: str, html: bool = False, also_console: bool = False):

162

"""Write INFO level message."""

163

164

def warn(msg: str, html: bool = False):

165

"""Write WARN level message (also shown in console)."""

166

167

def error(msg: str, html: bool = False):

168

"""Write ERROR level message (also shown in console)."""

169

170

def console(msg: str, newline: bool = True, stream: str = "stdout"):

171

"""Write message directly to console."""

172

```

173

174

**Usage Examples:**

175

176

```python

177

from robot.api import logger

178

179

class DatabaseLibrary:

180

181

def connect_to_database(self, connection_string):

182

logger.info(f"Connecting to database: {connection_string}")

183

184

try:

185

self.connection = self._establish_connection(connection_string)

186

logger.debug("Database connection established successfully")

187

except Exception as e:

188

logger.error(f"Failed to connect to database: {e}")

189

raise

190

191

def execute_query(self, query):

192

logger.trace(f"Executing SQL query: {query}")

193

194

if "DROP" in query.upper():

195

logger.warn("Executing potentially destructive query", html=False)

196

197

result = self.connection.execute(query)

198

logger.info(f"Query returned {len(result)} rows")

199

200

# Log HTML table of results

201

html_table = self._format_as_html_table(result)

202

logger.info(html_table, html=True)

203

204

return result

205

206

def backup_database(self):

207

logger.console("Starting database backup...", stream="stdout")

208

# Backup logic here

209

logger.console("Database backup completed!", stream="stdout")

210

```

211

212

### Library Interface Base Classes

213

214

Optional base classes for implementing different library APIs.

215

216

```python { .api }

217

class DynamicLibrary:

218

"""

219

Base class for libraries using the dynamic library API.

220

Keywords are discovered and executed dynamically.

221

"""

222

223

def get_keyword_names(self) -> Sequence[str]:

224

"""Return names of keywords this library implements (required)."""

225

226

def run_keyword(self, name: str, args: tuple, named: dict):

227

"""Execute the specified keyword with given arguments (required)."""

228

229

def get_keyword_documentation(self, name: str) -> str:

230

"""Return keyword documentation (optional)."""

231

232

def get_keyword_arguments(self, name: str) -> Sequence:

233

"""Return keyword argument specification (optional)."""

234

235

def get_keyword_types(self, name: str):

236

"""Return keyword argument types (optional)."""

237

238

def get_keyword_tags(self, name: str) -> Sequence[str]:

239

"""Return keyword tags (optional)."""

240

241

def get_keyword_source(self, name: str) -> str:

242

"""Return keyword source information (optional)."""

243

244

class HybridLibrary:

245

"""

246

Base class for libraries using the hybrid library API.

247

Combines static and dynamic approaches.

248

"""

249

250

def get_keyword_names(self) -> Sequence[str]:

251

"""Return names of keywords this library implements (required)."""

252

```

253

254

**Usage Examples:**

255

256

```python

257

from robot.api.interfaces import DynamicLibrary

258

259

class RestApiLibrary(DynamicLibrary):

260

261

def __init__(self, base_url=""):

262

self.base_url = base_url

263

self.session = self._create_session()

264

265

def get_keyword_names(self):

266

return ['GET Request', 'POST Request', 'PUT Request', 'DELETE Request',

267

'Set Base URL', 'Set Headers', 'Verify Status Code']

268

269

def run_keyword(self, name, args, named):

270

method_name = name.lower().replace(' ', '_')

271

method = getattr(self, method_name)

272

return method(*args, **named)

273

274

def get_keyword_documentation(self, name):

275

docs = {

276

'GET Request': 'Send HTTP GET request to specified endpoint',

277

'POST Request': 'Send HTTP POST request with data',

278

'Verify Status Code': 'Verify the HTTP response status code'

279

}

280

return docs.get(name, '')

281

282

def get_keyword_arguments(self, name):

283

args = {

284

'GET Request': ['endpoint', 'params={}', 'headers={}'],

285

'POST Request': ['endpoint', 'data', 'headers={}'],

286

'Verify Status Code': ['expected_code']

287

}

288

return args.get(name, [])

289

290

def get_keyword_tags(self, name):

291

if 'Request' in name:

292

return ['http', 'api']

293

return ['verification']

294

295

# Keyword implementations

296

def get_request(self, endpoint, params=None, headers=None):

297

url = f"{self.base_url}{endpoint}"

298

response = self.session.get(url, params=params, headers=headers)

299

self.last_response = response

300

return response

301

302

def verify_status_code(self, expected_code):

303

actual_code = self.last_response.status_code

304

if actual_code != int(expected_code):

305

raise Failure(f"Expected status {expected_code}, got {actual_code}")

306

```

307

308

### Listener Interfaces

309

310

Monitor and react to test execution events.

311

312

```python { .api }

313

class ListenerV2:

314

"""

315

Base class for listener API version 2.

316

Provides hooks for test execution events.

317

"""

318

319

def start_suite(self, name, attributes): ...

320

def end_suite(self, name, attributes): ...

321

def start_test(self, name, attributes): ...

322

def end_test(self, name, attributes): ...

323

def start_keyword(self, name, attributes): ...

324

def end_keyword(self, name, attributes): ...

325

def log_message(self, message): ...

326

def message(self, message): ...

327

def library_import(self, name, attributes): ...

328

def resource_import(self, name, attributes): ...

329

def variables_import(self, name, attributes): ...

330

def output_file(self, path): ...

331

def close(self): ...

332

333

class ListenerV3:

334

"""

335

Base class for listener API version 3.

336

Enhanced version with additional event information.

337

"""

338

339

# Similar methods to ListenerV2 but with enhanced attributes

340

def start_suite(self, data, result): ...

341

def end_suite(self, data, result): ...

342

def start_test(self, data, result): ...

343

def end_test(self, data, result): ...

344

# Additional methods for control structures

345

def start_if(self, data, result): ...

346

def end_if(self, data, result): ...

347

def start_for(self, data, result): ...

348

def end_for(self, data, result): ...

349

```

350

351

**Usage Examples:**

352

353

```python

354

from robot.api.interfaces import ListenerV2

355

import time

356

357

class TestTimingListener(ListenerV2):

358

359

def __init__(self):

360

self.suite_times = {}

361

self.test_times = {}

362

363

def start_suite(self, name, attributes):

364

self.suite_times[name] = {'start': time.time()}

365

print(f"Suite '{name}' started")

366

367

def end_suite(self, name, attributes):

368

start_time = self.suite_times[name]['start']

369

duration = time.time() - start_time

370

print(f"Suite '{name}' completed in {duration:.2f} seconds")

371

372

def start_test(self, name, attributes):

373

self.test_times[name] = {'start': time.time()}

374

375

def end_test(self, name, attributes):

376

start_time = self.test_times[name]['start']

377

duration = time.time() - start_time

378

status = attributes['status']

379

print(f"Test '{name}' {status} in {duration:.2f} seconds")

380

381

def log_message(self, message):

382

level = message['level']

383

if level == 'ERROR':

384

print(f"ERROR LOGGED: {message['message']}")

385

```

386

387

### Custom Parser Interface

388

389

Create custom parsers for non-standard test data formats.

390

391

```python { .api }

392

class Parser:

393

"""

394

Base class for custom parsers.

395

"""

396

397

extension: Union[str, Sequence[str]] # File extensions this parser handles

398

399

def parse(self, source: Path, defaults) -> TestSuite:

400

"""Parse source file into TestSuite (required)."""

401

402

def parse_init(self, source: Path, defaults) -> TestSuite:

403

"""Parse suite initialization file (optional)."""

404

```

405

406

**Usage Examples:**

407

408

```python

409

from robot.api.interfaces import Parser

410

from robot.api import TestSuite

411

import json

412

from pathlib import Path

413

414

class JsonTestParser(Parser):

415

extension = '.json'

416

417

def parse(self, source: Path, defaults):

418

with open(source) as f:

419

data = json.load(f)

420

421

suite = TestSuite(

422

name=data.get('name', source.stem),

423

doc=data.get('documentation', ''),

424

source=str(source)

425

)

426

427

# Parse test cases from JSON

428

for test_data in data.get('tests', []):

429

test = suite.tests.create(

430

name=test_data['name'],

431

doc=test_data.get('doc', '')

432

)

433

434

# Add keywords from JSON structure

435

for keyword_data in test_data.get('keywords', []):

436

test.keywords.create(

437

name=keyword_data['name'],

438

args=keyword_data.get('args', [])

439

)

440

441

return suite

442

```

443

444

## Types

445

446

```python { .api }

447

# Library scope options

448

Scope = Literal["GLOBAL", "SUITE", "TEST", "TASK"]

449

450

# Documentation format options

451

DocFormat = Literal["ROBOT", "HTML", "TEXT", "REST"]

452

453

# Logging levels

454

LOGLEVEL = Literal["TRACE", "DEBUG", "INFO", "CONSOLE", "HTML", "WARN", "ERROR"]

455

456

# Library interface types

457

Name = str

458

Arguments = Sequence[Union[str, Tuple[str], Tuple[str, Any]]]

459

Documentation = str

460

Tags = Sequence[str]

461

Source = str

462

463

# Parser interface types

464

TestDefaults = object # Default values and settings for test execution

465

466

# Type hint specifications

467

TypeHint = Union[type, str, Tuple["TypeHint", ...]]

468

TypeHints = Union[Mapping[str, TypeHint], Sequence[TypeHint]]

469

470

# Converter function signature

471

Converter = Union[Callable[[Any], Any], Callable[[Any, Any], Any]]

472

```