@@ -7,21 +7,22 @@ GitHub repository, but this isn't necessary to read for Python testing.
7
7
8
8
## Table of contents
9
9
10
- - [ Guide for test proxy troubleshooting] ( #guide-for-test-proxy-troubleshooting )
11
- - [ Table of contents] ( #table-of-contents )
12
- - [ Debugging tip] ( #debugging-tip )
13
- - [ Test collection failure] ( #test-collection-failure )
14
- - [ Errors in tests using resource preparers] ( #errors-in-tests-using-resource-preparers )
15
- - [ Test failure during ` record/start ` or ` playback/start ` requests] ( #test-failure-during-recordstart-or-playbackstart-requests )
16
- - [ Playback failures from body matching errors] ( #playback-failures-from-body-matching-errors )
17
- - [ Playback failures from inconsistent line breaks] ( #playback-failures-from-inconsistent-line-breaks )
18
- - [ Playback failures from URL mismatches] ( #playback-failures-from-url-mismatches )
19
- - [ Recordings not being produced] ( #recordings-not-being-produced )
20
- - [ ConnectionError during tests] ( #connectionerror-during-tests )
21
- - [ Different error than expected when using proxy] ( #different-error-than-expected-when-using-proxy )
22
- - [ Test setup failure in test pipeline] ( #test-setup-failure-in-test-pipeline )
23
- - [ Fixture not found error] ( #fixture-not-found-error )
24
- - [ PermissionError during startup] ( #permissionerror-during-startup )
10
+ - [ Debugging tip] ( #debugging-tip )
11
+ - [ ServiceRequestError: Cannot connect to host] ( #servicerequesterror-cannot-connect-to-host )
12
+ - [ ResourceNotFoundError: Playback failure] ( #resourcenotfounderror-playback-failure )
13
+ - [ Test collection failure] ( #test-collection-failure )
14
+ - [ Errors in tests using resource preparers] ( #errors-in-tests-using-resource-preparers )
15
+ - [ Test failure during ` record/start ` or ` playback/start ` requests] ( #test-failure-during-recordstart-or-playbackstart-requests )
16
+ - [ Playback failures from body matching errors] ( #playback-failures-from-body-matching-errors )
17
+ - [ Playback failures from inconsistent line breaks] ( #playback-failures-from-inconsistent-line-breaks )
18
+ - [ Playback failures from URL mismatches] ( #playback-failures-from-url-mismatches )
19
+ - [ Playback failures from inconsistent test values] ( #playback-failures-from-inconsistent-test-values )
20
+ - [ Recordings not being produced] ( #recordings-not-being-produced )
21
+ - [ ConnectionError during tests] ( #connectionerror-during-tests )
22
+ - [ Different error than expected when using proxy] ( #different-error-than-expected-when-using-proxy )
23
+ - [ Test setup failure in test pipeline] ( #test-setup-failure-in-test-pipeline )
24
+ - [ Fixture not found error] ( #fixture-not-found-error )
25
+ - [ PermissionError during startup] ( #permissionerror-during-startup )
25
26
26
27
## Debugging tip
27
28
@@ -39,6 +40,53 @@ containing the strings `test_delete` or `test_upload`.
39
40
40
41
For more information about ` pytest ` invocations, refer to [ Usage and Invocations] [ pytest_commands ] .
41
42
43
+ ## ServiceRequestError: Cannot connect to host
44
+
45
+ Tests may fail during startup with the following exception:
46
+
47
+ ``` text
48
+ azure.core.exceptions.ServiceRequestError: Cannot connect to host localhost:5001
49
+ ssl:True [SSLCertVerificationError: (1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate
50
+ verify failed: self signed certificate (_ssl.c:1123)')]
51
+ ```
52
+
53
+ This is caused by the test proxy's certificate being incorrectly configured. First, update your branch to include the
54
+ latest changes from ` main ` -- this ensures you have the latest certificate version (it needs to be occasionally
55
+ rotated).
56
+
57
+ If tests continue to fail, this is likely due to an async-specific environment issue. The certificate is
58
+ [ automatically configured] [ cert_setup ] during proxy startup, but async environments can still nondeterministically fail.
59
+
60
+ To work around this, set the following environment variable in your ` .env ` file:
61
+
62
+ ``` text
63
+ PROXY_URL='http://localhost:5000'
64
+ ```
65
+
66
+ This will target an HTTP endpoint for the test proxy that doesn't require certificates. Service requests will still be
67
+ sent securely from your client; this change only affects test proxy interactions.
68
+
69
+ ## ResourceNotFoundError: Playback failure
70
+
71
+ Test playback errors typically raise with a message similar to the following:
72
+
73
+ ``` text
74
+ FAILED test_client.py::TestClient::test_client_method - azure.core.exceptions.ResourceNotFoundError:
75
+ Playback failure -- for help resolving, see https://aka.ms/azsdk/python/test-proxy/troubleshoot. Error details:
76
+ Unable to find a record for the request POST https://fake_resource.service.azure.net?api-version=2025-09-01
77
+ ```
78
+
79
+ This means that the test recording didn't contain a match for the incoming playback request. This usually just means
80
+ that the test needs to be re-recorded to pick up library updates (e.g. a new service API version).
81
+
82
+ If playback errors persist after re-recording, you may need to modify session sanitizers or matchers. The following
83
+ sections of this guide describe common scenarios:
84
+
85
+ - [ Playback failures from body matching errors] ( #playback-failures-from-body-matching-errors )
86
+ - [ Playback failures from inconsistent line breaks] ( #playback-failures-from-inconsistent-line-breaks )
87
+ - [ Playback failures from URL mismatches] ( #playback-failures-from-url-mismatches )
88
+ - [ Playback failures from inconsistent test values] ( #playback-failures-from-inconsistent-test-values )
89
+
42
90
## Test collection failure
43
91
44
92
Make sure that all test class names begin with "Test", and that all test method names begin with "test_ ". For more
@@ -111,9 +159,9 @@ Resource preparers need a management client to function, so test classes that us
111
159
112
160
## Test failure during ` record/start ` or ` playback/start ` requests
113
161
114
- If your library uses out-of-repo recordings and tests fail during startup, logs might indicate that POST requests to
115
- ` record/start ` or ` playback/start ` endpoints are returning 500 responses. In a stack trace, these errors might be raised
116
- [ here] [ record_request_failure ] or [ here ] [ playback_request_failure ] , respectively.
162
+ If tests fail during startup, logs might indicate that POST requests to ` record/start ` or ` playback/start ` endpoints
163
+ are returning 500 responses. In a stack trace, these errors might be raised [ here ] [ record_request_failure ] or
164
+ [ here] [ playback_request_failure ] , respectively.
117
165
118
166
This suggests that the test proxy failed to fetch recordings from the assets repository. This likely comes from a
119
167
corrupted ` git ` configuration in ` azure-sdk-for-python/.assets ` . To resolve this:
@@ -139,11 +187,9 @@ These folders will be freshly recreated the next time you run tests.
139
187
140
188
## Playback failures from body matching errors
141
189
142
- In the old, ` vcrpy ` -based testing system, request and response bodies weren't compared in playback mode by default in
143
- most packages. The test proxy system enables body matching by default, which can introduce failures for tests that
144
- passed in the old system. For example, if a test sends a request that includes the current Unix time in its body, the
145
- body will contain a new value when run in playback mode at a later time. This request might still match the recording if
146
- body matching is disabled, but not if it's enabled.
190
+ The test proxy system enables body matching by default. For example, if a test sends a request that includes the
191
+ current Unix time in its body, the body will contain a new value when run in playback mode at a later time -- this
192
+ request won't match the recording if body matching is enabled.
147
193
148
194
Body matching can be turned off with the test proxy by calling the ` set_bodiless_matcher ` method from
149
195
[ devtools_testutils/sanitizers.py] [ py_sanitizers ] at the very start of a test method. This matcher applies only to the
@@ -152,13 +198,12 @@ matching enabled by default.
152
198
153
199
## Playback failures from inconsistent line breaks
154
200
155
- Some tests require recording content to completely match, including line breaks (for example, when sending the content of
156
- a test file in a request body). Line breaks can vary between OSes and cause tests to fail on certain platforms, in which
157
- case it can help to specify a particular format for test files by using [ ` .gitattributes ` ] [ gitattributes ] .
201
+ Line breaks can vary between OSes and cause tests to fail on certain platforms, in which case it can help to specify a
202
+ particular format for test files by using [ ` .gitattributes ` ] [ gitattributes ] .
158
203
159
- A ` .gitattributes ` file can be placed at the root of a directory to apply git settings to each file under that directory.
160
- If a test directory contains files that need to have consistent line breaks, for example LF breaks instead of CRLF ones,
161
- you can create a ` .gitattributes ` file in the directory with the following content:
204
+ A ` .gitattributes ` file can be placed at the root of a directory to apply git settings to each file under that
205
+ directory. If a test directory contains files that need to have consistent line breaks, for example LF breaks instead
206
+ of CRLF ones, you can create a ` .gitattributes ` file in the directory with the following content:
162
207
163
208
``` text
164
209
# Force git to checkout text files with LF (line feed) as the ending (vs CRLF)
@@ -209,11 +254,16 @@ that test, which is recommended.
209
254
210
255
### Sanitization impacting request URL/body/headers
211
256
212
- In some cases, a value in a response body is used in the following request as part of the URL, body, or headers. If this value is sanitized, the recorded request might differ than what is expected during playback. Common culprits include sanitization of "name", "id", and "Location" fields. To resolve this, you can either opt out of specific sanitization or add another sanitizer to align with the sanitized value.
257
+ In some cases, a value in a response body is used in the following request as part of the URL, body, or headers. If
258
+ this value is sanitized, the recorded request might differ than what is expected during playback. Common culprits
259
+ include sanitization of "name", "id", and "Location" fields. To resolve this, you can either opt out of specific
260
+ sanitization or add another sanitizer to align with the sanitized value.
213
261
214
262
#### Opt out
215
263
216
- You can opt out of sanitization for the fields that are used for your requests by calling the ` remove_batch_sanitizer ` method from ` devtools_testutils ` with the [ sanitizer IDs] [ test_proxy_sanitizers ] to exclude. Generally, this is done in the ` conftest.py ` file, in the one of the session-scoped fixtures. Example:
264
+ You can opt out of sanitization for the fields that are used for your requests by calling the ` remove_batch_sanitizer `
265
+ method from ` devtools_testutils ` with the [ sanitizer IDs] [ test_proxy_sanitizers ] to exclude. Generally, this is done in
266
+ the ` conftest.py ` file, in the one of the session-scoped fixtures. Example:
217
267
218
268
``` python
219
269
from devtools_testutils import remove_batch_sanitizers, test_proxy
@@ -245,6 +295,48 @@ from devtools_testutils import add_uri_regex_sanitizer
245
295
add_uri_regex_sanitizer(regex = " (?<=https://.+/foo/bar/)(?<id>[^/?\\ .]+)" , group_for_replace = " id" , value = " Sanitized" )
246
296
```
247
297
298
+ ## Playback failures from inconsistent test values
299
+
300
+ To run recorded tests successfully when recorded values are inconsistent or random and can't be sanitized, the test
301
+ proxy provides a ` variables ` API. This makes it possible for a test to record the values of variables that were used
302
+ during recording and use the same values in playback mode without a sanitizer.
303
+
304
+ For example, imagine that a test uses a randomized ` table_uuid ` variable when creating resources. The same random value
305
+ for ` table_uuid ` can be used in playback mode by using this ` variables ` API.
306
+
307
+ There are two requirements for a test to use recorded variables. First, the test method should accept ` **kwargs ` .
308
+ Second, the test method should ` return ` a dictionary with any test variables that it wants to record. This dictionary
309
+ will be stored in the recording when the test is run live, and will be passed to the test as a ` variables ` keyword
310
+ argument when the test is run in playback.
311
+
312
+ Below is a code example of how a test method could use recorded variables:
313
+
314
+ ``` python
315
+ from devtools_testutils import AzureRecordedTestCase, recorded_by_proxy
316
+
317
+ class TestExample (AzureRecordedTestCase ):
318
+
319
+ @recorded_by_proxy
320
+ def test_example (self , ** kwargs ):
321
+ # In live mode, variables is an empty dictionary
322
+ # In playback mode, the value of variables is {"table_uuid": "random-value"}
323
+ variables = kwargs.pop(" variables" , {})
324
+
325
+ # To fetch variable values, use the `setdefault` method to look for a key ("table_uuid")
326
+ # and set a real value for that key if it's not present ("random-value")
327
+ table_uuid = variables.setdefault(" table_uuid" , " random-value" )
328
+
329
+ # use variables["table_uuid"] when using the table UUID throughout the test
330
+ ...
331
+
332
+ # return the variables at the end of the test to record them
333
+ return variables
334
+ ```
335
+
336
+ > ** Note:** ` variables ` will be passed as a named argument to any test that accepts ` kwargs ` by the test proxy. In
337
+ > environments that don't use the test proxy, though -- like live test pipelines -- ` variables ` won't be provided.
338
+ > To avoid a KeyError, providing an empty dictionary as the default value to ` kwargs.pop ` is recommended.
339
+
248
340
## Recordings not being produced
249
341
250
342
Ensure the environment variable ` AZURE_SKIP_LIVE_RECORDING ` ** isn't** set to "true", and that ` AZURE_TEST_RUN_LIVE `
@@ -321,8 +413,8 @@ skipped, so the `TestProxy` parameter doesn't need to be set in `tests.yml`.
321
413
322
414
Tests that aren't recorded should omit the ` recorded_by_proxy ` decorator. However, if these unrecorded tests accept
323
415
parameters that are provided by a preparer like the ` devtools_testutils ` [ EnvironmentVariableLoader] [ env_var_loader ] ,
324
- you may see a new test setup error after migrating to the test proxy . For example, imagine a test is decorated with a
325
- preparer that provides a Key Vault URL as a ` azure_keyvault_url ` parameter:
416
+ you may see a test setup error. For example, imagine a test is decorated with a preparer that provides a Key Vault URL
417
+ as a ` azure_keyvault_url ` parameter:
326
418
327
419
``` python
328
420
class TestExample (AzureRecordedTestCase ):
@@ -369,7 +461,8 @@ Alternatively, you can delete the installed tool and re-run your tests to automa
369
461
370
462
<!-- Links -->
371
463
372
- [ custom_default_matcher ] : https://github.com/Azure/azure-sdk-for-python/blob/497f5f3435162c4f2086d1429fc1bba4f31a4354/eng/tools/azure-sdk-tools/devtools_testutils/sanitizers.py#L85
464
+ [ cert_setup ] : https://github.com/Azure/azure-sdk-for-python/blob/9958caf6269247f940c697a3f982bbbf0a47a19b/eng/tools/azure-sdk-tools/devtools_testutils/proxy_startup.py#L210
465
+ [ custom_default_matcher ] : https://github.com/Azure/azure-sdk-for-python/blob/9958caf6269247f940c697a3f982bbbf0a47a19b/eng/tools/azure-sdk-tools/devtools_testutils/sanitizers.py#L90
373
466
[ detailed_docs ] : https://github.com/Azure/azure-sdk-tools/tree/main/tools/test-proxy/Azure.Sdk.Tools.TestProxy/README.md
374
467
[ env_var_loader ] : https://github.com/Azure/azure-sdk-for-python/blob/main/eng/tools/azure-sdk-tools/devtools_testutils/envvariable_loader.py
375
468
[ gitattributes ] : https://git-scm.com/docs/gitattributes
@@ -379,10 +472,10 @@ Alternatively, you can delete the installed tool and re-run your tests to automa
379
472
[ parametrize_example ] : https://github.com/Azure/azure-sdk-for-python/blob/aa607b3b8c3e646928375ebcc6339d68e4e90a49/sdk/keyvault/azure-keyvault-keys/tests/test_key_client.py#L190
380
473
[ pipelines_ci ] : https://github.com/Azure/azure-sdk-for-python/blob/5ba894966ed6b0e1ee8d854871f8c2da36a73d79/sdk/eventgrid/ci.yml#L30
381
474
[ pipelines_live ] : https://github.com/Azure/azure-sdk-for-python/blob/e2b5852deaef04752c1323d2ab0958f83b98858f/sdk/textanalytics/tests.yml#L26-L27
382
- [ playback_request_failure ] : https://github.com/Azure/azure-sdk-for-python/blob/e23d9a6b1edcc1127ded40b9993029495b4ad08c /eng/tools/azure-sdk-tools/devtools_testutils/proxy_testcase.py#L108
475
+ [ playback_request_failure ] : https://github.com/Azure/azure-sdk-for-python/blob/9958caf6269247f940c697a3f982bbbf0a47a19b /eng/tools/azure-sdk-tools/devtools_testutils/proxy_testcase.py#L102
383
476
[ py_sanitizers ] : https://github.com/Azure/azure-sdk-for-python/blob/main/eng/tools/azure-sdk-tools/devtools_testutils/sanitizers.py
384
477
[ pytest_collection ] : https://docs.pytest.org/latest/goodpractices.html#test-discovery
385
478
[ pytest_commands ] : https://docs.pytest.org/latest/usage.html
386
- [ record_request_failure ] : https://github.com/Azure/azure-sdk-for-python/blob/e23d9a6b1edcc1127ded40b9993029495b4ad08c /eng/tools/azure-sdk-tools/devtools_testutils/proxy_testcase.py#L97
479
+ [ record_request_failure ] : https://github.com/Azure/azure-sdk-for-python/blob/9958caf6269247f940c697a3f982bbbf0a47a19b /eng/tools/azure-sdk-tools/devtools_testutils/proxy_testcase.py#L91
387
480
[ test_proxy_sanitizers ] : https://github.com/Azure/azure-sdk-tools/blob/57382d5dc00b10a2f9cfd597293eeee0c2dbd8fd/tools/test-proxy/Azure.Sdk.Tools.TestProxy/Common/SanitizerDictionary.cs#L65
388
481
[ wrong_exception ] : https://github.com/Azure/azure-sdk-tools/issues/2907
0 commit comments