or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

ansi-styling.mddebug.mdindex.mdpretty-printing.mdpytest-plugin.mdtiming.md

pytest-plugin.mddocs/

0

# Pytest Plugin

1

2

Pytest integration for enhanced testing workflows with automatic assert statement insertion, test failure reporting, and development-time debugging utilities. The plugin provides `insert_assert()` functionality for dynamic test generation and integrates devtools debugging capabilities into the pytest environment.

3

4

## Capabilities

5

6

### Insert Assert Function

7

8

Dynamically insert assert statements during test development for rapid test creation and debugging.

9

10

```python { .api }

11

def insert_assert(value) -> int:

12

"""

13

Insert assert statement for testing (requires Python 3.8+).

14

15

Analyzes the calling code and generates an assert statement comparing

16

the provided value with its current runtime value. The generated assert

17

is formatted and can be written to the source file.

18

19

Parameters:

20

- value: Value to create assert statement for

21

22

Returns:

23

Number of insert_assert calls in the current test

24

25

Raises:

26

RuntimeError: If Python version < 3.8 or code inspection fails

27

"""

28

```

29

30

Usage examples:

31

32

```python

33

def test_calculation():

34

result = calculate_something(10, 20)

35

36

# During development, use insert_assert to capture expected values

37

insert_assert(result) # Generates: assert result == 42

38

39

# After running tests, the insert_assert calls are replaced with actual asserts

40

assert result == 42

41

42

def test_data_processing():

43

data = process_data([1, 2, 3, 4, 5])

44

45

# Multiple insert_assert calls in one test

46

insert_assert(len(data)) # Generates: assert len(data) == 5

47

insert_assert(data[0]) # Generates: assert data[0] == 2

48

insert_assert(sum(data)) # Generates: assert sum(data) == 15

49

50

# After processing:

51

assert len(data) == 5

52

assert data[0] == 2

53

assert sum(data) == 15

54

55

def test_complex_object():

56

obj = create_complex_object()

57

58

# Works with complex expressions

59

insert_assert(obj.property.method())

60

# Generates: assert obj.property.method() == expected_value

61

```

62

63

### Pytest Configuration Options

64

65

Command-line options for controlling insert_assert behavior.

66

67

```bash

68

# Print generated assert statements instead of writing to files

69

pytest --insert-assert-print

70

71

# Fail tests that contain insert_assert calls (for CI/CD)

72

pytest --insert-assert-fail

73

```

74

75

### Pytest Fixtures

76

77

Built-in fixtures for accessing insert_assert functionality and integration.

78

79

```python { .api }

80

@pytest.fixture(name='insert_assert')

81

def insert_assert_fixture() -> Callable[[Any], int]:

82

"""

83

Pytest fixture providing access to insert_assert function.

84

85

Returns:

86

insert_assert function for use in test functions

87

"""

88

89

@pytest.fixture(scope='session', autouse=True)

90

def insert_assert_add_to_builtins() -> None:

91

"""

92

Automatically add insert_assert and debug to builtins for all tests.

93

94

Makes insert_assert and debug available without imports in test files.

95

"""

96

97

@pytest.fixture(autouse=True)

98

def insert_assert_maybe_fail(pytestconfig: pytest.Config) -> Generator[None, None, None]:

99

"""

100

Auto-use fixture that optionally fails tests containing insert_assert calls.

101

102

Controlled by --insert-assert-fail command line option.

103

"""

104

105

@pytest.fixture(scope='session', autouse=True)

106

def insert_assert_session(pytestconfig: pytest.Config) -> Generator[None, None, None]:

107

"""

108

Session-level fixture handling insert_assert code generation and file writing.

109

110

Manages the actual insertion of assert statements into source files.

111

"""

112

```

113

114

Usage with fixtures:

115

116

```python

117

def test_with_fixture(insert_assert):

118

# Use insert_assert fixture explicitly

119

result = some_calculation()

120

count = insert_assert(result)

121

assert count == 1 # First insert_assert call in this test

122

123

def test_builtin_access():

124

# insert_assert available without imports due to auto-fixture

125

data = get_test_data()

126

insert_assert(data['status']) # Works without explicit import

127

128

# debug also available

129

debug(data) # Works without explicit import

130

```

131

132

### Pytest Hooks

133

134

Plugin hooks for customizing test behavior and reporting.

135

136

```python { .api }

137

def pytest_addoption(parser) -> None:

138

"""

139

Add command-line options for insert_assert functionality.

140

141

Adds:

142

- --insert-assert-print: Print statements instead of writing files

143

- --insert-assert-fail: Fail tests with insert_assert calls

144

"""

145

146

def pytest_report_teststatus(report: pytest.TestReport, config: pytest.Config):

147

"""

148

Custom test status reporting for insert_assert failures.

149

150

Shows 'INSERT ASSERT' status for tests failed due to insert_assert calls.

151

152

Returns:

153

Tuple of (outcome, letter, verbose) for insert_assert failures

154

"""

155

156

def pytest_terminal_summary() -> None:

157

"""

158

Print summary of insert_assert operations at end of test session.

159

160

Shows statistics about generated assert statements and file modifications.

161

"""

162

```

163

164

### Development Workflow Integration

165

166

Typical workflow for using insert_assert in test development:

167

168

```python

169

# 1. Initial test development

170

def test_new_feature():

171

result = new_feature_function()

172

173

# Use insert_assert to capture actual values during development

174

insert_assert(result.status)

175

insert_assert(result.data)

176

insert_assert(len(result.items))

177

178

# 2. Run tests with print mode to see generated asserts

179

# pytest test_file.py --insert-assert-print

180

181

# 3. Review printed assert statements:

182

# --------------------------------------------------------------------------------

183

# test_file.py - 5:

184

# --------------------------------------------------------------------------------

185

# # insert_assert(result.status)

186

# assert result.status == 'success'

187

# --------------------------------------------------------------------------------

188

189

# 4. Run tests normally to write asserts to file

190

# pytest test_file.py

191

192

# 5. Final test after insert_assert replacement:

193

def test_new_feature():

194

result = new_feature_function()

195

196

# insert_assert calls replaced with actual assertions

197

assert result.status == 'success'

198

assert result.data == {'key': 'value'}

199

assert len(result.items) == 3

200

```

201

202

### Error Handling and Edge Cases

203

204

The plugin handles various edge cases gracefully:

205

206

```python

207

def test_error_handling():

208

# Works with complex expressions

209

obj = get_complex_object()

210

insert_assert(obj.nested.property[0].method())

211

212

# Handles exceptions during formatting

213

problematic_value = get_problematic_value()

214

insert_assert(problematic_value) # Won't crash even if repr() fails

215

216

# Works with custom objects

217

custom = CustomClass()

218

insert_assert(custom) # Uses appropriate representation

219

220

def test_multiple_calls_same_line():

221

x, y = 1, 2

222

# Multiple insert_assert calls - handles duplicates

223

insert_assert(x); insert_assert(y)

224

# Second call on same line will be skipped in file writing

225

```

226

227

### Integration with Devtools Debug

228

229

Seamless integration with devtools debug functionality:

230

231

```python

232

def test_with_debug():

233

# Both debug and insert_assert available in test environment

234

data = process_test_data()

235

236

# Debug for inspection during development

237

debug(data)

238

239

# insert_assert for generating test assertions

240

insert_assert(data['result'])

241

insert_assert(data['error_count'])

242

243

def test_debug_in_fixtures(insert_assert):

244

# Debug works in pytest fixtures and test functions

245

@pytest.fixture

246

def test_data():

247

data = create_test_data()

248

debug(data) # Available without import

249

return data

250

```

251

252

### File Modification and Code Generation

253

254

The plugin automatically handles source file modification:

255

256

- **Black Integration**: Generated code is formatted using Black configuration from `pyproject.toml`

257

- **Duplicate Handling**: Prevents multiple asserts on the same line

258

- **Reversible Operation**: Original `insert_assert` calls are replaced, not deleted

259

- **Safe Writing**: Files are only modified when tests pass

260

261

```python

262

# Configuration via pyproject.toml affects generated code formatting

263

[tool.black]

264

line-length = 88

265

target-version = ['py38']

266

267

# Generated assert statements respect Black configuration:

268

# assert very_long_variable_name_here == {

269

# "key1": "value1",

270

# "key2": "value2"

271

# }

272

```

273

274

### Testing Insert Assert Itself

275

276

For testing the insert_assert functionality:

277

278

```python

279

def test_insert_assert_behavior():

280

# Test that insert_assert returns correct count

281

count1 = insert_assert(42)

282

count2 = insert_assert("hello")

283

284

assert count1 == 1

285

assert count2 == 2

286

287

def test_insert_assert_with_expressions():

288

data = [1, 2, 3]

289

290

# Test with expressions

291

insert_assert(len(data))

292

insert_assert(sum(data))

293

insert_assert(data[0])

294

295

# These will generate appropriate assert statements

296

```

297

298

## Types

299

300

```python { .api }

301

@dataclass

302

class ToReplace:

303

"""

304

Represents a code replacement operation for insert_assert.

305

"""

306

file: Path # Source file to modify

307

start_line: int # Starting line number

308

end_line: int | None # Ending line number

309

code: str # Replacement code

310

311

class PlainRepr(str):

312

"""

313

String class where repr() doesn't include quotes.

314

Used for custom representation in generated assert statements.

315

"""

316

def __repr__(self) -> str: ...

317

318

# Context variables for tracking insert_assert state

319

insert_assert_calls: ContextVar[int] # Count of calls in current test

320

insert_assert_summary: ContextVar[list[str]] # Session summary information

321

322

# Global state

323

to_replace: list[ToReplace] # List of pending code replacements

324

```