or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

core-runner.mddjango-integration.mdindex.mdjenkins-compatibility.mdresult-collection.mdxml-utilities.md

result-collection.mddocs/

0

# Result Collection and XML Generation

1

2

Comprehensive test result collection system that captures detailed test execution information and generates XML reports compatible with JUnit schema and CI/CD systems.

3

4

## Capabilities

5

6

### XMLTestResult Class

7

8

Main result collector that extends unittest's TextTestResult to capture test outcomes, timing information, stdout/stderr, and generate XML reports.

9

10

```python { .api }

11

class _XMLTestResult(TextTestResult):

12

def __init__(self, stream=sys.stderr, descriptions=1, verbosity=1,

13

elapsed_times=True, properties=None, infoclass=None):

14

"""

15

Initialize XML test result collector.

16

17

Parameters:

18

- stream: file-like object for text output

19

- descriptions: int, description verbosity (0=no, 1=short, 2=long)

20

- verbosity: int, output verbosity level

21

- elapsed_times: bool, track test execution timing

22

- properties: dict or None, JUnit testsuite properties

23

- infoclass: class or None, custom test info class

24

"""

25

26

def generate_reports(self, test_runner):

27

"""Generate XML reports using the test runner's configuration."""

28

29

def addSuccess(self, test):

30

"""Record successful test completion."""

31

32

def addFailure(self, test, err):

33

"""Record test failure with exception information."""

34

35

def addError(self, test, err):

36

"""Record test error with exception information."""

37

38

def addSkip(self, test, reason):

39

"""Record skipped test with reason."""

40

41

def addSubTest(self, testcase, test, err):

42

"""Record subtest result (limited support)."""

43

44

def addExpectedFailure(self, test, err):

45

"""Record expected test failure."""

46

47

def addUnexpectedSuccess(self, test):

48

"""Record unexpected test success."""

49

```

50

51

#### Usage Examples

52

53

**Custom Result Class**

54

```python

55

import xmlrunner

56

from xmlrunner.result import _XMLTestResult

57

58

class CustomTestResult(_XMLTestResult):

59

def addSuccess(self, test):

60

super().addSuccess(test)

61

print(f"✓ {test.id()}")

62

63

runner = xmlrunner.XMLTestRunner(

64

output='reports',

65

resultclass=CustomTestResult

66

)

67

```

68

69

**JUnit Properties**

70

```python

71

import unittest

72

import xmlrunner

73

74

# Add custom properties to testsuite

75

class TestWithProperties(unittest.TestCase):

76

def setUp(self):

77

# Properties can be set on test suite

78

if not hasattr(self.__class__, 'properties'):

79

self.__class__.properties = {

80

'build_number': '123',

81

'environment': 'staging',

82

'branch': 'main'

83

}

84

85

def test_example(self):

86

self.assertTrue(True)

87

88

unittest.main(testRunner=xmlrunner.XMLTestRunner(output='reports'))

89

```

90

91

### TestInfo Class

92

93

Container for detailed test execution information, used internally by _XMLTestResult to track test outcomes and metadata.

94

95

```python { .api }

96

class _TestInfo:

97

# Test outcome constants

98

SUCCESS: int = 0

99

FAILURE: int = 1

100

ERROR: int = 2

101

SKIP: int = 3

102

103

def __init__(self, test_result, test_method, outcome=SUCCESS, err=None,

104

subTest=None, filename=None, lineno=None, doc=None):

105

"""

106

Initialize test information container.

107

108

Parameters:

109

- test_result: _XMLTestResult instance

110

- test_method: test method object

111

- outcome: int, test outcome (SUCCESS/FAILURE/ERROR/SKIP)

112

- err: tuple or str, exception information

113

- subTest: subtest object or None

114

- filename: str or None, test file path

115

- lineno: int or None, test line number

116

- doc: str or None, test method docstring

117

"""

118

119

def test_finished(self):

120

"""Finalize test information after test completion."""

121

122

def get_error_info(self):

123

"""Get formatted exception information."""

124

125

def id(self):

126

"""Get test identifier."""

127

128

# Attributes populated during test execution

129

test_result: _XMLTestResult

130

outcome: int

131

elapsed_time: float

132

timestamp: str

133

test_name: str

134

test_id: str

135

test_description: str

136

test_exception_name: str

137

test_exception_message: str

138

test_exception_info: str

139

stdout: str

140

stderr: str

141

filename: str | None

142

lineno: int | None

143

doc: str | None

144

```

145

146

### Output Capture

147

148

The result system captures stdout/stderr during test execution for inclusion in XML reports.

149

150

```python { .api }

151

class _DuplicateWriter:

152

def __init__(self, first, second):

153

"""

154

Dual-output writer that duplicates output to two streams.

155

156

Parameters:

157

- first: primary output stream

158

- second: secondary stream (typically StringIO for capture)

159

"""

160

161

def write(self, data):

162

"""Write data to both streams."""

163

164

def flush(self):

165

"""Flush both streams."""

166

167

def getvalue(self):

168

"""Get captured value from secondary stream."""

169

```

170

171

#### Usage Example

172

173

```python

174

import io

175

from xmlrunner.result import _DuplicateWriter

176

177

# Capture output while still displaying to console

178

capture = io.StringIO()

179

dual_writer = _DuplicateWriter(sys.stdout, capture)

180

181

# Use dual_writer for test output

182

# Later retrieve captured content with capture.getvalue()

183

```

184

185

### XML Report Generation

186

187

The result system generates XML reports following JUnit schema with support for multiple output formats.

188

189

#### XML Structure

190

191

```xml

192

<?xml version="1.0" encoding="UTF-8"?>

193

<testsuites>

194

<testsuite name="test_module.TestClass" tests="3" failures="1" errors="0"

195

skipped="0" time="0.123" timestamp="2023-12-01T10:30:00">

196

<properties>

197

<property name="build_number" value="123"/>

198

</properties>

199

<testcase classname="test_module.TestClass" name="test_success"

200

time="0.001" timestamp="2023-12-01T10:30:00"/>

201

<testcase classname="test_module.TestClass" name="test_failure"

202

time="0.002" timestamp="2023-12-01T10:30:01">

203

<failure type="AssertionError" message="False is not true">

204

<![CDATA[Traceback (most recent call last):

205

File "test_module.py", line 10, in test_failure

206

self.assertTrue(False)

207

AssertionError: False is not true]]>

208

</failure>

209

<system-out><![CDATA[Test output here]]></system-out>

210

</testcase>

211

</testsuite>

212

</testsuites>

213

```

214

215

### Constants and Utility Functions

216

217

```python { .api }

218

# Format constants for output capture

219

STDOUT_LINE: str = '\nStdout:\n%s'

220

STDERR_LINE: str = '\nStderr:\n%s'

221

222

def safe_unicode(data, encoding='utf8'):

223

"""

224

Convert data to unicode string with only valid XML characters.

225

226

Parameters:

227

- data: input data to convert

228

- encoding: encoding for byte strings

229

230

Returns:

231

- str: cleaned unicode string

232

"""

233

234

def testcase_name(test_method):

235

"""

236

Extract test case name from test method.

237

238

Parameters:

239

- test_method: test method object

240

241

Returns:

242

- str: test case name in format 'module.TestClass'

243

"""

244

245

def resolve_filename(filename):

246

"""

247

Make filename relative to current directory when possible.

248

249

Parameters:

250

- filename: str, file path

251

252

Returns:

253

- str: relative or absolute filename

254

"""

255

```

256

257

### Integration with Test Discovery

258

259

The result system works seamlessly with unittest's test discovery:

260

261

```python

262

import unittest

263

import xmlrunner

264

265

# Test discovery with XML reporting

266

loader = unittest.TestLoader()

267

suite = loader.discover('tests', pattern='test_*.py')

268

269

runner = xmlrunner.XMLTestRunner(output='test-reports')

270

result = runner.run(suite)

271

272

# Access result information

273

print(f"Tests run: {result.testsRun}")

274

print(f"Failures: {len(result.failures)}")

275

print(f"Errors: {len(result.errors)}")

276

print(f"Skipped: {len(result.skipped)}")

277

```

278

279

### SubTest Support

280

281

Limited support for unittest.TestCase.subTest functionality:

282

283

```python

284

import unittest

285

import xmlrunner

286

287

class TestWithSubTests(unittest.TestCase):

288

def test_with_subtests(self):

289

for i in range(3):

290

with self.subTest(i=i):

291

self.assertEqual(i % 2, 0) # Will fail for i=1

292

293

# Note: SubTest granularity may be lost in XML reports

294

# due to JUnit schema limitations

295

unittest.main(testRunner=xmlrunner.XMLTestRunner(output='reports'))

296

```