Skip to content

Commit

Permalink
merge upstream
Browse files Browse the repository at this point in the history
  • Loading branch information
scbedd committed Feb 22, 2021
2 parents f230089 + 6fd75b3 commit b0c6469
Show file tree
Hide file tree
Showing 1,261 changed files with 261,295 additions and 92,804 deletions.
2 changes: 1 addition & 1 deletion .github/CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
/sdk/loganalytics/ @alexeldeib

# PRLabel: %Monitor - Exporter
/sdk/monitor/azure-opentelemetry-exporter-azuremonitor @rakshith91 @lmazuel @hectorhdzg @lzchen
/sdk/monitor/azure-monitor-opentelemetry-exporter @rakshith91 @lmazuel @lzchen

# PRLabel: %Consumption
/sdk/consumption/ @sandeepnl
Expand Down
70 changes: 70 additions & 0 deletions doc/dev/tests-advanced.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,80 @@
# Setup Python Development Environment - Advanced
In this document we will provide additional information about the test environments:

- [Test Mixin Classes](#test-mixin-classes)
- [Resource Preparers](#preparers)
- [Examples with Preparers](#examples-with-preparers)
- [mgmt_settings_real.py](#mgmt_settings_real-file)

## Test Mixin Classes
Many of our test suites use a mixin class to reduce re-writing code in multiple test files. For example, in the Tables test suite there is a `_shared` directory containing two of these mixin classes, a [sync one](https://github.com/Azure/azure-sdk-for-python/blob/master/sdk/tables/azure-data-tables/tests/_shared/testcase.py) and an [async version](https://github.com/Azure/azure-sdk-for-python/blob/master/sdk/tables/azure-data-tables/tests/_shared/asynctestcase.py). These classes will often have ways to create connection strings from an account name and key, formulate the account url, configure logging, or validate service responses. In order for these mixin classes to be used by both the functional and unit tests they should inherit from `object`. For example:

```python

class TablesTestMixin(object):
def connection_string(self, account, key):
return "DefaultEndpointsProtocol=https;AccountName=" + account + ";AccountKey=" + str(key) + ";EndpointSuffix=core.windows.net"

def account_url(self, account, endpoint_type):
"""Return an url of storage account.
:param str storage_account: Storage account name
:param str storage_type: The Storage type part of the URL. Should be "table", or "cosmos", etc.
"""
try:
if endpoint_type == "table":
return account.primary_endpoints.table.rstrip("/")
if endpoint_type == "cosmos":
return "https://{}.table.cosmos.azure.com".format(account.name)
else:
raise ValueError("Unknown storage type {}".format(storage_type))
except AttributeError: # Didn't find "primary_endpoints"
if endpoint_type == "table":
return 'https://{}.{}.core.windows.net'.format(account, endpoint_type)
if endpoint_type == "cosmos":
return "https://{}.table.cosmos.azure.com".format(account)

def enable_logging(self):
handler = logging.StreamHandler()
handler.setFormatter(logging.Formatter(LOGGING_FORMAT))
self.logger.handlers = [handler]
self.logger.setLevel(logging.INFO)
self.logger.propagate = True
self.logger.disabled = False
```

In action this class can be used in functional tests:

```python
class TestTablesFunctional(AzureTestCase, TablesTestMixin):
...
def test_with_mixin(self, account, key):
conn_str = self.connection_string(account, key)
client = TableClient.from_connection_string(conn_str)
client.create_table('first')
client.create_table('second')
tables = 0
for table in client.list_tables():
tables += 1

assert tables == 2
```

Or can be used in a unit test:
```python
class TestTablesUnit(TablesTestMixin):
...
def test_valid_url(self):
account = "fake_tables_account"
credential = "fake_tables_account_key_0123456789"

url = self.account_url(account, "tables")
client = TableClient(account_url=url, credential=credential)

assert client is not None
assert client.account_url == "https://{}.tables.core.windows.net/".format(account)
```


## Preparers
The Azure SDK team has created some in house tools to help with easier testing. These additional tools are located in the `devtools_testutils` package that was installed with your `dev_requirements.txt`. In this package are the preparers that will be commonly used throughout the repository to test various resources. A preparer is a way to programmatically create fresh resources to run our tests against and then deleting them after running a test suite. These help guarantee standardized behavior by starting each test group from a fresh resource and account.

Expand Down
62 changes: 48 additions & 14 deletions doc/dev/tests.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ azure-sdk-for-python\sdk\my-directory\my-library> tox -c ../../../eng/tox/tox.in
azure-sdk-for-python\sdk\my-directory\my-library> tox -c ../../../eng/tox/tox.ini -e whl
azure-sdk-for-python\sdk\my-directory\my-library> tox -c ../../../eng/tox/tox.ini -e sdist
azure-sdk-for-python\sdk\my_directory\my_library> tox -c ../../../eng/tox/tox.ini -e samples
azure-sdk-for-python\sdk\my_directory\my_library> tox -c ../../../eng/tox/tox.ini -e apistub
```
A quick description of the five commands above:
* sphinx: documentation generation using the inline comments written in our code
Expand All @@ -121,9 +122,10 @@ A quick description of the five commands above:
* whl: creates a whl package for installing our package
* sdist: creates a zipped distribution of our files that the end user could install with pip
* samples: runs all of the samples in the `samples` directory and verifies they are working correctly
* apistub: runs the [apistubgenerator](https://github.com/Azure/azure-sdk-tools/tree/master/packages/python-packages/api-stub-generator) tool on your code

## `devtools_testutils` Package
The Azure SDK team has created some in house tools to help with easier testing. These additional tools are located in the `devtools_testutils` package that was installed with your `dev_requirements.txt`. In this package is the `AzureTestCase` object which every test case object should inherit from. This management object takes care of creating and scrubbing recordings to make sure secrets are not added to the recordings files (and subsequently to the git history) and authenticating clients for test methods.
The Azure SDK team has created some in house tools to help with easier testing. These additional tools are located in the `devtools_testutils` package that was installed with your `dev_requirements.txt`. In this package is the [`AzureTestCase`](https://github.com/Azure/azure-sdk-for-python/blob/master/tools/azure-sdk-tools/devtools_testutils/azure_testcase.py#L99-L350) object which every test case object should inherit from. This management object takes care of creating and scrubbing recordings to make sure secrets are not added to the recordings files (and subsequently to the git history) and authenticating clients for test methods.

## Writing New Tests
SDK tests are based on the `scenario_tests` subpackage located in [`azure-sdk-for-python/tools/azure-devtools/src/azure_devtools`](https://pypi.org/project/azure-devtools/). `scenario_tests` is a general, mostly abstracted framework which provides several useful features for writing SDK tests, ie:
Expand Down Expand Up @@ -264,32 +266,64 @@ From your terminal run the `pytest` command to run all the tests that you have w

Your update should run smooth and have green dots representing passing tests. Now if you look at the contents of your `tests` directory there should be a new directory called `recording` with four `.yaml` files. Each `yaml` file is a recording for a single test. To run a test in playback mode change the `testsettings_local.cfg` to `live-mode: false` and rerun the tests with the same command. The test infrastructure will use the automatically created `.yaml` recordings to mock the HTTP traffic and run the tests.

## Functional vs. Unit Tests

## More Test Examples
The test written above is a functional test, it generates HTTP traffic and sends data to the service. Most of our clients have some client-side validation for account names, formatting, or properties that do not generate HTTP traffic. For unit tests, the best practice is to have a separate test class from the `AzureTestCase` class which tests client side validation methods. For example, the `azure-data-tables` library has client-side validation for the table name and properties of the entity, below is an example of how these could be tested:

This section will demonstrate how to write tests with the `devtools_testutils` package with a few samples to showcase the features of the test framework.
```python
import pytest
from azure.data.tables import TableServiceClient, EntityProperty, EdmType

class TestTablesUnitTest(object):

def test_invalid_table_name(self):
account_name = 'fake_account_name'
account_key = 'fake_account_key1234567890'
tsc = TableServiceClient(
account_url='https://{}.table.core.windows.net/'.format(account_name),
credential=account_key
)

invalid_table_name = "bad_table_name" # table name cannot have an '_' character

with pytest.raises(ValueError):
tsc.create_table(invalid_table_name)

def test_entity_properties(self):
ep = EntityProperty('abc', EdmType.STRING)
ep = EntityProperty(b'abc', EdmType.BINARY)
ep = EntityProperty(1.2345, EdmType.DOUBLE)

### Example 1: Basic Recording and Interactions
with pytest.raises(ValueError):
ep = EntityProperty(2 ** 75, EdmType.Int64) # Tables can only handle integers up to 2 ^ 63
```

Async tests need to be marked with a `@pytest.mark.asyncio` to be properly handled. For example:
```python
import os
import pytest
from azure.data.tables.aio import TableServiceClient

from azure.data.tables import TableServiceClient
from devtools_testutils import AzureTestCase
class TestTablesUnitTest(object):

class ExampleStorageTestCase(AzureTestCase):
@pytest.mark.asyncio
async def test_invalid_table_name(self):
account_name = 'fake_account_name'
account_key = 'fake_account_key1234567890'
tsc = TableServiceClient(
account_url='https://{}.table.core.windows.net/'.format(account_name),
credential=account_key
)

def test_create_table(self):
credential = self.get_credential(TableServiceClient)
client = self.create_client_from_credential(TableServiceClient, credential, account_url=self.account_url)
invalid_table_name = "bad_table_name" # table name cannot have an '_' character

with pytest.raises(ValueError):
table_name = "an_invalid_table"
created = client.create_table(table_name)
await tsc.create_table(invalid_table_name)
```

This simple tests that the client verifies a valid table name before sending the HTTP request (Azure Data Tables can only be alphanumeric values). This test will have no recording associated with it.

## More Test Examples

This section will demonstrate how to write tests with the `devtools_testutils` package with a few samples to showcase the features of the test framework.

For more information, refer to the [advanced tests notes][advanced_tests_notes] on more advanced scenarios and additional information.

Expand Down
Loading

0 comments on commit b0c6469

Please sign in to comment.