or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

configuration.mddata-loading.mddatabase-connections.mdexternal-server.mdindex.mdprocess-management.md

database-connections.mddocs/

0

# Database Connections

1

2

Client fixture factory for creating database connections with automatic cleanup, transaction isolation, and database management between tests. Provides psycopg connections with comprehensive lifecycle management.

3

4

## Capabilities

5

6

### Client Fixture Factory

7

8

Creates database connection fixtures that automatically manage database lifecycle and cleanup.

9

10

```python { .api }

11

def postgresql(

12

process_fixture_name: str,

13

dbname: Optional[str] = None,

14

isolation_level: Optional[psycopg.IsolationLevel] = None,

15

) -> Callable[[FixtureRequest], Iterator[Connection]]:

16

"""

17

Create a postgresql client fixture factory.

18

19

Parameters:

20

- process_fixture_name: Name of the process or noproc fixture to depend on

21

- dbname: Override database name (default: uses process fixture's dbname)

22

- isolation_level: Transaction isolation level (default: server default)

23

24

Returns:

25

Function that creates psycopg.Connection fixture with function scope

26

"""

27

```

28

29

### Database Janitor

30

31

Manages database lifecycle including creation, population, and cleanup.

32

33

```python { .api }

34

class DatabaseJanitor:

35

"""

36

Database lifecycle manager for test isolation.

37

38

Handles database creation, initialization, and cleanup to ensure

39

test isolation and repeatability.

40

"""

41

42

def __init__(

43

self,

44

*,

45

user: str,

46

host: str,

47

port: Union[str, int],

48

version: Union[str, float, Version],

49

dbname: str,

50

template_dbname: str,

51

password: Optional[str] = None,

52

isolation_level: Optional[psycopg.IsolationLevel] = None,

53

connection_timeout: int = 60,

54

): ...

55

56

def init(self) -> None: ...

57

def drop(self) -> None: ...

58

def load(self, load_list: List[Union[Callable, str, Path]]) -> None: ...

59

def cursor(self) -> Iterator[Cursor]: ...

60

def is_template(self) -> bool: ...

61

62

def __enter__(self) -> "DatabaseJanitor": ...

63

def __exit__(

64

self,

65

exc_type: Optional[Type[BaseException]],

66

exc_val: Optional[BaseException],

67

exc_tb: Optional[TracebackType],

68

) -> None: ...

69

```

70

71

## Usage Examples

72

73

### Basic Client Connection

74

75

```python

76

from pytest_postgresql import factories

77

78

# Create process and client fixtures

79

postgresql_proc = factories.postgresql_proc()

80

postgresql = factories.postgresql('postgresql_proc')

81

82

def test_database_connection(postgresql):

83

"""Test basic database connection."""

84

cur = postgresql.cursor()

85

cur.execute("SELECT version();")

86

result = cur.fetchone()

87

assert result is not None

88

cur.close()

89

```

90

91

### Custom Database Name

92

93

```python

94

from pytest_postgresql import factories

95

96

postgresql_proc = factories.postgresql_proc(dbname='main_db')

97

postgresql_custom = factories.postgresql('postgresql_proc', dbname='custom_test_db')

98

99

def test_custom_database(postgresql_custom, postgresql_proc):

100

"""Test connection to custom database."""

101

cur = postgresql_custom.cursor()

102

cur.execute("SELECT current_database();")

103

db_name = cur.fetchone()[0]

104

assert db_name == 'custom_test_db'

105

cur.close()

106

```

107

108

### Transaction Isolation

109

110

```python

111

import psycopg

112

from pytest_postgresql import factories

113

114

postgresql_proc = factories.postgresql_proc()

115

postgresql_serializable = factories.postgresql(

116

'postgresql_proc',

117

isolation_level=psycopg.IsolationLevel.SERIALIZABLE

118

)

119

120

def test_transaction_isolation(postgresql_serializable):

121

"""Test transaction isolation level."""

122

cur = postgresql_serializable.cursor()

123

cur.execute("SHOW transaction_isolation;")

124

isolation = cur.fetchone()[0]

125

assert isolation == 'serializable'

126

cur.close()

127

```

128

129

### Multiple Database Connections

130

131

```python

132

from pytest_postgresql import factories

133

134

postgresql_proc = factories.postgresql_proc()

135

postgresql_db1 = factories.postgresql('postgresql_proc', dbname='db1')

136

postgresql_db2 = factories.postgresql('postgresql_proc', dbname='db2')

137

138

def test_multiple_databases(postgresql_db1, postgresql_db2):

139

"""Test connections to multiple databases."""

140

# Create table in first database

141

cur1 = postgresql_db1.cursor()

142

cur1.execute("CREATE TABLE test1 (id INT);")

143

postgresql_db1.commit()

144

cur1.close()

145

146

# Create different table in second database

147

cur2 = postgresql_db2.cursor()

148

cur2.execute("CREATE TABLE test2 (id INT);")

149

postgresql_db2.commit()

150

cur2.close()

151

152

# Verify isolation between databases

153

cur1 = postgresql_db1.cursor()

154

cur1.execute("SELECT tablename FROM pg_tables WHERE schemaname='public';")

155

tables1 = [row[0] for row in cur1.fetchall()]

156

assert 'test1' in tables1

157

assert 'test2' not in tables1

158

cur1.close()

159

```

160

161

### Database Operations

162

163

```python

164

def test_database_operations(postgresql):

165

"""Test various database operations."""

166

cur = postgresql.cursor()

167

168

# Create table

169

cur.execute("""

170

CREATE TABLE users (

171

id SERIAL PRIMARY KEY,

172

name VARCHAR(100) NOT NULL,

173

email VARCHAR(100) UNIQUE,

174

created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP

175

);

176

""")

177

178

# Insert data

179

cur.execute(

180

"INSERT INTO users (name, email) VALUES (%s, %s) RETURNING id;",

181

('John Doe', 'john@example.com')

182

)

183

user_id = cur.fetchone()[0]

184

185

# Query data

186

cur.execute("SELECT name, email FROM users WHERE id = %s;", (user_id,))

187

user = cur.fetchone()

188

assert user[0] == 'John Doe'

189

assert user[1] == 'john@example.com'

190

191

# Update data

192

cur.execute(

193

"UPDATE users SET name = %s WHERE id = %s;",

194

('Jane Doe', user_id)

195

)

196

197

# Delete data

198

cur.execute("DELETE FROM users WHERE id = %s;", (user_id,))

199

200

postgresql.commit()

201

cur.close()

202

```

203

204

### Context Manager Usage

205

206

```python

207

def test_context_manager(postgresql):

208

"""Test using connection as context manager."""

209

with postgresql:

210

with postgresql.cursor() as cur:

211

cur.execute("CREATE TABLE temp_table (data TEXT);")

212

cur.execute("INSERT INTO temp_table (data) VALUES ('test');")

213

214

cur.execute("SELECT data FROM temp_table;")

215

result = cur.fetchone()

216

assert result[0] == 'test'

217

# Connection automatically committed/rolled back

218

```

219

220

### Connection Properties

221

222

```python

223

def test_connection_properties(postgresql):

224

"""Test connection properties and information."""

225

# Connection status

226

assert postgresql.status == psycopg.pq.ConnStatus.CONNECTION_OK

227

228

# Server information

229

info = postgresql.info

230

assert info.server_version > 0

231

assert info.host is not None

232

assert info.port > 0

233

234

# Database information

235

cur = postgresql.cursor()

236

cur.execute("SELECT current_database(), current_user, version();")

237

db_name, user, version = cur.fetchone()

238

assert db_name is not None

239

assert user is not None

240

assert 'PostgreSQL' in version

241

cur.close()

242

```

243

244

### Error Handling

245

246

```python

247

import psycopg

248

from psycopg import errors

249

250

def test_error_handling(postgresql):

251

"""Test database error handling."""

252

cur = postgresql.cursor()

253

254

try:

255

# This will fail - table doesn't exist

256

cur.execute("SELECT * FROM nonexistent_table;")

257

except errors.UndefinedTable as e:

258

assert 'nonexistent_table' in str(e)

259

260

try:

261

# This will fail - syntax error

262

cur.execute("INVALID SQL SYNTAX;")

263

except errors.SyntaxError as e:

264

assert 'syntax error' in str(e).lower()

265

266

# Connection should still be usable after errors

267

cur.execute("SELECT 1;")

268

result = cur.fetchone()

269

assert result[0] == 1

270

271

cur.close()

272

```

273

274

### Batch Operations

275

276

```python

277

def test_batch_operations(postgresql):

278

"""Test batch database operations."""

279

cur = postgresql.cursor()

280

281

# Create table

282

cur.execute("""

283

CREATE TABLE products (

284

id SERIAL PRIMARY KEY,

285

name VARCHAR(100),

286

price DECIMAL(10,2)

287

);

288

""")

289

290

# Batch insert

291

products = [

292

('Product A', 19.99),

293

('Product B', 29.99),

294

('Product C', 39.99)

295

]

296

297

cur.executemany(

298

"INSERT INTO products (name, price) VALUES (%s, %s);",

299

products

300

)

301

302

# Verify batch insert

303

cur.execute("SELECT COUNT(*) FROM products;")

304

count = cur.fetchone()[0]

305

assert count == 3

306

307

postgresql.commit()

308

cur.close()

309

```

310

311

## Connection Lifecycle

312

313

### Automatic Cleanup

314

315

The client fixture automatically handles cleanup between tests:

316

317

1. **After each test**: Closes all open connections and drops the test database

318

2. **Template database**: Maintains a template database for fast test database creation

319

3. **Isolation**: Each test gets a fresh database instance

320

321

### Manual Database Management

322

323

```python

324

from pytest_postgresql.janitor import DatabaseJanitor

325

326

def test_manual_database_management(postgresql_proc):

327

"""Test manual database management with DatabaseJanitor."""

328

janitor = DatabaseJanitor(

329

user=postgresql_proc.user,

330

host=postgresql_proc.host,

331

port=postgresql_proc.port,

332

version=postgresql_proc.version,

333

dbname='manual_test_db',

334

template_dbname=postgresql_proc.template_dbname,

335

password=postgresql_proc.password

336

)

337

338

with janitor:

339

# Database is created and ready

340

with janitor.cursor() as cur:

341

cur.execute("CREATE TABLE manual_test (id INT);")

342

cur.execute("INSERT INTO manual_test (id) VALUES (1);")

343

344

cur.execute("SELECT id FROM manual_test;")

345

result = cur.fetchone()

346

assert result[0] == 1

347

# Database is automatically dropped

348

```