or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

auth.mdchannel.mddiscovery.mderrors.mdhttp.mdindex.mdmedia.mdmimeparse.mdmodel.mdschema.mdtesting.md

testing.mddocs/

0

# Testing and Mocking

1

2

The Google API Python Client provides comprehensive testing utilities including HTTP mocking, response simulation, and sequence testing to enable effective unit testing without making real API calls.

3

4

## Capabilities

5

6

### HTTP Mocking Classes

7

8

Mock HTTP responses for testing API interactions without network calls.

9

10

```python { .api }

11

class HttpMock:

12

"""Mock HTTP responses for testing Google API operations."""

13

14

def __init__(self, filename=None, headers=None):

15

"""

16

Initialize HTTP mock with static response.

17

18

Args:

19

filename (str, optional): Path to file containing mock response content

20

headers (dict, optional): HTTP headers to include in mock response

21

"""

22

23

def request(self, uri, method='GET', body=None, headers=None,

24

redirections=1, connection_type=None):

25

"""

26

Return a mock HTTP response.

27

28

Args:

29

uri (str): Request URI (ignored in basic mock)

30

method (str): HTTP method (ignored in basic mock)

31

body (str, optional): Request body (ignored in basic mock)

32

headers (dict, optional): Request headers (ignored in basic mock)

33

redirections (int): Max redirections (ignored in basic mock)

34

connection_type: Connection type (ignored in basic mock)

35

36

Returns:

37

tuple: (httplib2.Response, content) - mock response and content

38

"""

39

40

class HttpMockSequence:

41

"""Mock a sequence of HTTP responses for testing multiple requests."""

42

43

def __init__(self, iterable):

44

"""

45

Initialize mock sequence with predefined responses.

46

47

Args:

48

iterable: Sequence of (response, content) tuples to return in order

49

"""

50

51

def request(self, uri, method='GET', body=None, headers=None,

52

redirections=1, connection_type=None):

53

"""

54

Return the next mock HTTP response in the sequence.

55

56

Args:

57

uri (str): Request URI (logged but ignored)

58

method (str): HTTP method (logged but ignored)

59

body (str, optional): Request body (logged but ignored)

60

headers (dict, optional): Request headers (logged but ignored)

61

redirections (int): Max redirections (ignored)

62

connection_type: Connection type (ignored)

63

64

Returns:

65

tuple: (httplib2.Response, content) - next mock response and content

66

67

Raises:

68

StopIteration: When all mock responses have been consumed

69

"""

70

```

71

72

## Usage Examples

73

74

### Basic HTTP Mocking

75

76

```python

77

from googleapiclient import discovery

78

from googleapiclient.http import HttpMock

79

import json

80

81

# Create mock response content

82

mock_response = {

83

'messages': [

84

{'id': '1', 'threadId': 'thread1'},

85

{'id': '2', 'threadId': 'thread2'}

86

]

87

}

88

89

# Create mock HTTP client

90

http_mock = HttpMock(headers={'status': '200'})

91

# You can also use a file: HttpMock('path/to/response.json')

92

93

# Build service with mock HTTP client

94

service = discovery.build('gmail', 'v1', http=http_mock)

95

96

# Make API call - returns mock data instead of real API call

97

result = service.users().messages().list(userId='me').execute()

98

print(result) # Will print the mock_response data

99

```

100

101

### File-Based Mocking

102

103

```python

104

from googleapiclient.http import HttpMock

105

import json

106

import tempfile

107

import os

108

109

# Create temporary mock response file

110

mock_data = {

111

'id': 'mock_message_id',

112

'payload': {

113

'headers': [

114

{'name': 'Subject', 'value': 'Mock Email Subject'},

115

{'name': 'From', 'value': 'test@example.com'}

116

]

117

}

118

}

119

120

with tempfile.NamedTemporaryFile(mode='w', suffix='.json', delete=False) as f:

121

json.dump(mock_data, f)

122

mock_file = f.name

123

124

try:

125

# Create mock with file

126

http_mock = HttpMock(filename=mock_file, headers={'status': '200'})

127

service = discovery.build('gmail', 'v1', http=http_mock)

128

129

# Test API call

130

message = service.users().messages().get(userId='me', id='test_id').execute()

131

print(f"Subject: {message['payload']['headers'][0]['value']}")

132

133

finally:

134

# Clean up

135

os.unlink(mock_file)

136

```

137

138

### Sequence Mocking for Multiple Requests

139

140

```python

141

from googleapiclient.http import HttpMockSequence

142

import httplib2

143

import json

144

145

# Create a sequence of mock responses

146

responses = [

147

# First request - list messages

148

(

149

httplib2.Response({'status': 200}),

150

json.dumps({

151

'messages': [

152

{'id': 'msg1', 'threadId': 'thread1'},

153

{'id': 'msg2', 'threadId': 'thread2'}

154

]

155

}).encode('utf-8')

156

),

157

# Second request - get first message

158

(

159

httplib2.Response({'status': 200}),

160

json.dumps({

161

'id': 'msg1',

162

'payload': {

163

'headers': [

164

{'name': 'Subject', 'value': 'First Message'},

165

{'name': 'From', 'value': 'sender1@example.com'}

166

]

167

}

168

}).encode('utf-8')

169

),

170

# Third request - get second message

171

(

172

httplib2.Response({'status': 200}),

173

json.dumps({

174

'id': 'msg2',

175

'payload': {

176

'headers': [

177

{'name': 'Subject', 'value': 'Second Message'},

178

{'name': 'From', 'value': 'sender2@example.com'}

179

]

180

}

181

}).encode('utf-8')

182

)

183

]

184

185

# Create sequence mock

186

http_mock = HttpMockSequence(responses)

187

service = discovery.build('gmail', 'v1', http=http_mock)

188

189

# Make sequential API calls - each gets the next response

190

messages_list = service.users().messages().list(userId='me').execute()

191

print(f"Found {len(messages_list['messages'])} messages")

192

193

for msg_info in messages_list['messages']:

194

message = service.users().messages().get(userId='me', id=msg_info['id']).execute()

195

subject = next(h['value'] for h in message['payload']['headers'] if h['name'] == 'Subject')

196

print(f"Message: {subject}")

197

```

198

199

### Error Response Mocking

200

201

```python

202

from googleapiclient.http import HttpMock

203

from googleapiclient.errors import HttpError

204

import httplib2

205

import json

206

207

# Mock an error response

208

error_content = {

209

'error': {

210

'code': 404,

211

'message': 'Requested entity was not found.',

212

'errors': [

213

{

214

'domain': 'global',

215

'reason': 'notFound',

216

'message': 'Requested entity was not found.'

217

}

218

]

219

}

220

}

221

222

# Create mock with error response

223

http_mock = HttpMock(headers={'status': '404'})

224

service = discovery.build('gmail', 'v1', http=http_mock)

225

226

try:

227

# This will raise an HttpError with the mocked error

228

message = service.users().messages().get(userId='me', id='nonexistent').execute()

229

except HttpError as error:

230

print(f'Caught expected error: {error.status_code}')

231

print(f'Error reason: {error._get_reason()}')

232

```

233

234

### Unit Testing with pytest

235

236

```python

237

import pytest

238

from googleapiclient import discovery

239

from googleapiclient.http import HttpMock, HttpMockSequence

240

from googleapiclient.errors import HttpError

241

import json

242

import httplib2

243

244

class TestGmailService:

245

"""Test class for Gmail service operations."""

246

247

@pytest.fixture

248

def mock_service(self):

249

"""Create a Gmail service with HTTP mocking."""

250

http_mock = HttpMock(headers={'status': '200'})

251

return discovery.build('gmail', 'v1', http=http_mock)

252

253

def test_list_messages(self, mock_service):

254

"""Test listing messages with mock response."""

255

# The mock will return default response

256

result = mock_service.users().messages().list(userId='me').execute()

257

assert 'messages' in result or result == {} # Depends on mock setup

258

259

def test_get_message_success(self):

260

"""Test getting a message with successful mock response."""

261

mock_response = {

262

'id': 'test_message_id',

263

'payload': {

264

'headers': [

265

{'name': 'Subject', 'value': 'Test Subject'},

266

{'name': 'From', 'value': 'test@example.com'}

267

]

268

}

269

}

270

271

# Create mock with specific response

272

http_mock = HttpMock(headers={'status': '200'})

273

service = discovery.build('gmail', 'v1', http=http_mock)

274

275

# Mock returns predefined response

276

result = service.users().messages().get(userId='me', id='test_id').execute()

277

# In real implementation, you'd set up the mock to return mock_response

278

279

def test_http_error_handling(self):

280

"""Test HTTP error handling with mock error response."""

281

# Mock 404 error

282

http_mock = HttpMock(headers={'status': '404'})

283

service = discovery.build('gmail', 'v1', http=http_mock)

284

285

with pytest.raises(HttpError) as exc_info:

286

service.users().messages().get(userId='me', id='missing').execute()

287

288

assert exc_info.value.status_code == 404

289

290

def test_batch_operations(self):

291

"""Test batch operations with sequence mocking."""

292

responses = [

293

(httplib2.Response({'status': 200}), b'{"id": "msg1"}'),

294

(httplib2.Response({'status': 200}), b'{"id": "msg2"}'),

295

(httplib2.Response({'status': 404}), b'{"error": {"code": 404}}')

296

]

297

298

http_mock = HttpMockSequence(responses)

299

service = discovery.build('gmail', 'v1', http=http_mock)

300

301

# First two calls succeed, third fails

302

result1 = service.users().messages().get(userId='me', id='msg1').execute()

303

result2 = service.users().messages().get(userId='me', id='msg2').execute()

304

305

with pytest.raises(HttpError):

306

service.users().messages().get(userId='me', id='missing').execute()

307

```

308

309

### Integration Testing Pattern

310

311

```python

312

import unittest

313

from googleapiclient import discovery

314

from googleapiclient.http import HttpMockSequence

315

import httplib2

316

import json

317

318

class GmailApiTest(unittest.TestCase):

319

"""Integration tests for Gmail API operations."""

320

321

def setUp(self):

322

"""Set up test fixtures."""

323

self.test_messages = [

324

{

325

'id': 'msg1',

326

'payload': {'headers': [{'name': 'Subject', 'value': 'Test 1'}]}

327

},

328

{

329

'id': 'msg2',

330

'payload': {'headers': [{'name': 'Subject', 'value': 'Test 2'}]}

331

}

332

]

333

334

def test_message_workflow(self):

335

"""Test complete message retrieval workflow."""

336

# Create sequence of responses for workflow

337

responses = [

338

# List messages response

339

(

340

httplib2.Response({'status': 200}),

341

json.dumps({

342

'messages': [

343

{'id': 'msg1', 'threadId': 'thread1'},

344

{'id': 'msg2', 'threadId': 'thread2'}

345

]

346

}).encode('utf-8')

347

),

348

# Get message 1

349

(

350

httplib2.Response({'status': 200}),

351

json.dumps(self.test_messages[0]).encode('utf-8')

352

),

353

# Get message 2

354

(

355

httplib2.Response({'status': 200}),

356

json.dumps(self.test_messages[1]).encode('utf-8')

357

)

358

]

359

360

http_mock = HttpMockSequence(responses)

361

service = discovery.build('gmail', 'v1', http=http_mock)

362

363

# Execute workflow

364

messages_list = service.users().messages().list(userId='me').execute()

365

self.assertEqual(len(messages_list['messages']), 2)

366

367

# Get each message

368

for i, msg_info in enumerate(messages_list['messages']):

369

message = service.users().messages().get(

370

userId='me',

371

id=msg_info['id']

372

).execute()

373

374

expected_subject = f'Test {i + 1}'

375

actual_subject = message['payload']['headers'][0]['value']

376

self.assertEqual(actual_subject, expected_subject)

377

378

if __name__ == '__main__':

379

unittest.main()

380

```

381

382

### Advanced Mock Patterns

383

384

```python

385

from googleapiclient.http import HttpMock

386

import httplib2

387

import json

388

389

class CustomHttpMock:

390

"""Custom HTTP mock with request inspection and dynamic responses."""

391

392

def __init__(self):

393

self.requests = [] # Track all requests made

394

self.responses = {} # Map URI patterns to responses

395

396

def add_response(self, uri_pattern, response_data, status=200):

397

"""Add a response for a specific URI pattern."""

398

self.responses[uri_pattern] = (status, response_data)

399

400

def request(self, uri, method='GET', body=None, headers=None,

401

redirections=1, connection_type=None):

402

"""Mock request with dynamic response based on URI."""

403

404

# Log the request

405

self.requests.append({

406

'uri': uri,

407

'method': method,

408

'body': body,

409

'headers': headers

410

})

411

412

# Find matching response

413

for pattern, (status, data) in self.responses.items():

414

if pattern in uri:

415

response = httplib2.Response({'status': status})

416

content = json.dumps(data).encode('utf-8') if isinstance(data, dict) else data

417

return response, content

418

419

# Default response

420

response = httplib2.Response({'status': 200})

421

return response, b'{}'

422

423

def get_requests(self):

424

"""Get all requests that were made."""

425

return self.requests.copy()

426

427

# Usage example

428

mock = CustomHttpMock()

429

mock.add_response('messages', {'messages': []})

430

mock.add_response('labels', {'labels': [{'id': 'INBOX', 'name': 'INBOX'}]})

431

432

service = discovery.build('gmail', 'v1', http=mock)

433

434

# Make some requests

435

service.users().messages().list(userId='me').execute()

436

service.users().labels().list(userId='me').execute()

437

438

# Inspect what requests were made

439

requests = mock.get_requests()

440

print(f"Made {len(requests)} requests:")

441

for req in requests:

442

print(f" {req['method']} {req['uri']}")

443

```