## HI!
This is a framework for API autotests with coverage assessment. Detailed instructions in the process of writing. It is better to check with the author how to use it. Tools are used:
* pytest
* httpx
* allure
Files are required for specific work:
**conftest.py** - it must have a fixture's inside:
```commandline
@pytest.fixture(scope="session")
def api_client(domain):
return ApiClient(domain=domain)
```
```commandline
@pytest.fixture(scope='session', autouse=True)
def clear_call_data():
"""Фикстура для очистки данных перед запуском тестов."""
global call_count, call_type
api_call_storage.call_count.clear()
api_call_storage.call_type.clear()
yield
```
**confpartest.py** - It must have variables inside:
```
swagger_files = {
'test1': ['local', '../public/swagger/app-openapi.yaml'],
'test2': ['local', '../public/swagger/app-openapi2.yaml'],
'test3': ['url', 'https://url.ru']
}
test_types_coverage = ['default', '405', 'param']
```
The project must have a test that displays information about the coverage in allure. The name of it **test_zorro.py**:
```commandline
async def test_display_final_call_counts(self):
report_lines = []
total_coverage_percentage = 0
total_endpoints = 0
total_calls_excluding_generation = 0
for (method, endpoint, description), count in call_count.items():
types = set(call_type[(method, endpoint, description)])
total_endpoints += 1
# Подсчет вызовов, исключая тип 'generation_data'
if 'generation_data' not in types:
total_calls_excluding_generation += count
# Проверка на наличие обязательных типов тестов
coverage_status = "Недостаточное покрытие ❌"
matched_types = set(types).intersection(types)
count_matched = len(matched_types)
# Логика для определения статуса покрытия и расчета процента
if count_matched == len(types): # Все типы присутствуют
coverage_status = "Покрытие выполнено ✅"
total_coverage_percentage += 100
elif count_matched == 2:
coverage_status = "Покрытие выполнено на 66% 🔔"
total_coverage_percentage += 66
elif count_matched == 1:
coverage_status = "Покрытие выполнено на 33% ❌"
total_coverage_percentage += 33
else:
coverage_status = "Недостаточное покрытие ❌"
total_coverage_percentage += 0
report_line = (
f"\n{description}\nЭндпоинт: {endpoint}\nМетод: {method} | "
f"Обращений: {count}, Типы тестов: {', '.join(types)}\n{coverage_status}\n"
)
report_lines.append(report_line)
# Вычисление общего процента покрытия
if total_endpoints > 0:
average_coverage_percentage = total_coverage_percentage / total_endpoints
else:
average_coverage_percentage = 0
border = "*" * 50
summary = f"{border}\nОбщий процент покрытия: {average_coverage_percentage:.2f}%\nОбщее количество вызовов (исключая 'generation_data'): {total_calls_excluding_generation}\n{border}\n"
# Добавляем сводку в начало отчета
report_lines.insert(0, summary)
create_chart(call_count)
with open('api_call_counts.png', 'rb') as f:
allure.attach(f.read(), name='Оценка покрытия', attachment_type=allure.attachment_type.PNG)
allure.attach("\n".join(report_lines), name='Отчет по вызовам API', attachment_type=allure.attachment_type.TEXT)
assert True
```
What does the test look like:
```commandline
async def test_get(self, api_client):
endpoint = 'https://ya.ru'
response = await api_client.make_request(
'GET',
endpoint,
params='limit=1',
expected_status_code=200,
validate_model=Models.ValidateGet,
type=types.type_default
)
assert response is not None
assert isinstance(response, dict)
```
All available data that the client can accept:
```
method: str,
endpoint: str,
add_url1: Optional[str] = '',
add_url2: Optional[str] = '',
add_url3: Optional[str] = '',
params: Optional[Dict[str, Any]] = None,
headers: Optional[Dict[str, str]] = None,
data: Optional[Dict[str, Any]] = None,
expected_status_code: Optional[int] = None,
validate_model: Optional[Type[BaseModel]] = None,
type: Optional[str] = None```
Raw data
{
"_id": null,
"home_page": "https://github.com/Dec01/partest",
"name": "partest",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.8",
"maintainer_email": null,
"keywords": "autotest partest test coverage",
"author": "dec01",
"author_email": "parschin.ewg@yandex.ru",
"download_url": "https://files.pythonhosted.org/packages/30/fb/e100c134d1fd54e179a1002ef765d66cc21dfaf1ab180e88f6181c62ee20/partest-0.1.31.tar.gz",
"platform": null,
"description": "## HI!\n\nThis is a framework for API autotests with coverage assessment. Detailed instructions in the process of writing. It is better to check with the author how to use it. Tools are used:\n\n* pytest\n* httpx\n* allure\n\nFiles are required for specific work:\n\n**conftest.py** - it must have a fixture's inside:\n\n```commandline\n@pytest.fixture(scope=\"session\")\ndef api_client(domain):\n return ApiClient(domain=domain)\n```\n```commandline\n@pytest.fixture(scope='session', autouse=True)\ndef clear_call_data():\n \"\"\"\u0424\u0438\u043a\u0441\u0442\u0443\u0440\u0430 \u0434\u043b\u044f \u043e\u0447\u0438\u0441\u0442\u043a\u0438 \u0434\u0430\u043d\u043d\u044b\u0445 \u043f\u0435\u0440\u0435\u0434 \u0437\u0430\u043f\u0443\u0441\u043a\u043e\u043c \u0442\u0435\u0441\u0442\u043e\u0432.\"\"\"\n global call_count, call_type\n api_call_storage.call_count.clear()\n api_call_storage.call_type.clear()\n yield\n```\n\n**confpartest.py** - It must have variables inside:\n\n```\nswagger_files = {\n 'test1': ['local', '../public/swagger/app-openapi.yaml'],\n 'test2': ['local', '../public/swagger/app-openapi2.yaml'],\n 'test3': ['url', 'https://url.ru']\n}\n\ntest_types_coverage = ['default', '405', 'param']\n```\n\nThe project must have a test that displays information about the coverage in allure. The name of it **test_zorro.py**:\n\n```commandline\n\n async def test_display_final_call_counts(self):\n report_lines = []\n total_coverage_percentage = 0\n total_endpoints = 0\n total_calls_excluding_generation = 0\n\n for (method, endpoint, description), count in call_count.items():\n types = set(call_type[(method, endpoint, description)])\n total_endpoints += 1\n\n # \u041f\u043e\u0434\u0441\u0447\u0435\u0442 \u0432\u044b\u0437\u043e\u0432\u043e\u0432, \u0438\u0441\u043a\u043b\u044e\u0447\u0430\u044f \u0442\u0438\u043f 'generation_data'\n if 'generation_data' not in types:\n total_calls_excluding_generation += count\n\n # \u041f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 \u043d\u0430 \u043d\u0430\u043b\u0438\u0447\u0438\u0435 \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0445 \u0442\u0438\u043f\u043e\u0432 \u0442\u0435\u0441\u0442\u043e\u0432\n coverage_status = \"\u041d\u0435\u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e\u0435 \u043f\u043e\u043a\u0440\u044b\u0442\u0438\u0435 \u274c\"\n matched_types = set(types).intersection(types) \n count_matched = len(matched_types)\n\n # \u041b\u043e\u0433\u0438\u043a\u0430 \u0434\u043b\u044f \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u044f \u0441\u0442\u0430\u0442\u0443\u0441\u0430 \u043f\u043e\u043a\u0440\u044b\u0442\u0438\u044f \u0438 \u0440\u0430\u0441\u0447\u0435\u0442\u0430 \u043f\u0440\u043e\u0446\u0435\u043d\u0442\u0430\n if count_matched == len(types): # \u0412\u0441\u0435 \u0442\u0438\u043f\u044b \u043f\u0440\u0438\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u044e\u0442\n coverage_status = \"\u041f\u043e\u043a\u0440\u044b\u0442\u0438\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u043e \u2705\"\n total_coverage_percentage += 100\n elif count_matched == 2: \n coverage_status = \"\u041f\u043e\u043a\u0440\u044b\u0442\u0438\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u043e \u043d\u0430 66% \ud83d\udd14\"\n total_coverage_percentage += 66\n elif count_matched == 1: \n coverage_status = \"\u041f\u043e\u043a\u0440\u044b\u0442\u0438\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u043e \u043d\u0430 33% \u274c\"\n total_coverage_percentage += 33\n else: \n coverage_status = \"\u041d\u0435\u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e\u0435 \u043f\u043e\u043a\u0440\u044b\u0442\u0438\u0435 \u274c\"\n total_coverage_percentage += 0\n\n report_line = (\n f\"\\n{description}\\n\u042d\u043d\u0434\u043f\u043e\u0438\u043d\u0442: {endpoint}\\n\u041c\u0435\u0442\u043e\u0434: {method} | \"\n f\"\u041e\u0431\u0440\u0430\u0449\u0435\u043d\u0438\u0439: {count}, \u0422\u0438\u043f\u044b \u0442\u0435\u0441\u0442\u043e\u0432: {', '.join(types)}\\n{coverage_status}\\n\"\n )\n report_lines.append(report_line)\n\n # \u0412\u044b\u0447\u0438\u0441\u043b\u0435\u043d\u0438\u0435 \u043e\u0431\u0449\u0435\u0433\u043e \u043f\u0440\u043e\u0446\u0435\u043d\u0442\u0430 \u043f\u043e\u043a\u0440\u044b\u0442\u0438\u044f\n if total_endpoints > 0:\n average_coverage_percentage = total_coverage_percentage / total_endpoints\n else:\n average_coverage_percentage = 0\n\n border = \"*\" * 50\n summary = f\"{border}\\n\u041e\u0431\u0449\u0438\u0439 \u043f\u0440\u043e\u0446\u0435\u043d\u0442 \u043f\u043e\u043a\u0440\u044b\u0442\u0438\u044f: {average_coverage_percentage:.2f}%\\n\u041e\u0431\u0449\u0435\u0435 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0432\u044b\u0437\u043e\u0432\u043e\u0432 (\u0438\u0441\u043a\u043b\u044e\u0447\u0430\u044f 'generation_data'): {total_calls_excluding_generation}\\n{border}\\n\"\n\n # \u0414\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u0441\u0432\u043e\u0434\u043a\u0443 \u0432 \u043d\u0430\u0447\u0430\u043b\u043e \u043e\u0442\u0447\u0435\u0442\u0430\n report_lines.insert(0, summary)\n\n create_chart(call_count)\n\n with open('api_call_counts.png', 'rb') as f:\n allure.attach(f.read(), name='\u041e\u0446\u0435\u043d\u043a\u0430 \u043f\u043e\u043a\u0440\u044b\u0442\u0438\u044f', attachment_type=allure.attachment_type.PNG)\n\n allure.attach(\"\\n\".join(report_lines), name='\u041e\u0442\u0447\u0435\u0442 \u043f\u043e \u0432\u044b\u0437\u043e\u0432\u0430\u043c API', attachment_type=allure.attachment_type.TEXT)\n\n assert True\n\n```\n\n\nWhat does the test look like:\n\n```commandline\n async def test_get(self, api_client):\n endpoint = 'https://ya.ru'\n response = await api_client.make_request(\n 'GET',\n endpoint,\n params='limit=1',\n expected_status_code=200,\n validate_model=Models.ValidateGet,\n type=types.type_default\n )\n assert response is not None\n assert isinstance(response, dict)\n```\n\nAll available data that the client can accept:\n```\nmethod: str,\nendpoint: str,\nadd_url1: Optional[str] = '',\nadd_url2: Optional[str] = '',\nadd_url3: Optional[str] = '',\nparams: Optional[Dict[str, Any]] = None,\nheaders: Optional[Dict[str, str]] = None,\ndata: Optional[Dict[str, Any]] = None,\nexpected_status_code: Optional[int] = None,\nvalidate_model: Optional[Type[BaseModel]] = None,\ntype: Optional[str] = None```\n",
"bugtrack_url": null,
"license": null,
"summary": "This is a module for the rapid implementation of test cases with coverage tracking. This module contains a call counter for specific endpoints and their methods. As well as the function of determining the types of tests that need to be counted.",
"version": "0.1.31",
"project_urls": {
"GitHub": "https://github.com/Dec01/partest",
"Homepage": "https://github.com/Dec01/partest"
},
"split_keywords": [
"autotest",
"partest",
"test",
"coverage"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "6ec684fea8aad9a3d1d9b8b5e67e172383c6521130b73623bb4a871c68ce4e5c",
"md5": "37c63a970c2dc26596eef204c30b090e",
"sha256": "c9a55580d94c18d2d017ceced96af8f4b8810ef8bb326dfaee5ef750e5e6d0f0"
},
"downloads": -1,
"filename": "partest-0.1.31-py3-none-any.whl",
"has_sig": false,
"md5_digest": "37c63a970c2dc26596eef204c30b090e",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.8",
"size": 14147,
"upload_time": "2024-12-09T09:38:12",
"upload_time_iso_8601": "2024-12-09T09:38:12.705584Z",
"url": "https://files.pythonhosted.org/packages/6e/c6/84fea8aad9a3d1d9b8b5e67e172383c6521130b73623bb4a871c68ce4e5c/partest-0.1.31-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "30fbe100c134d1fd54e179a1002ef765d66cc21dfaf1ab180e88f6181c62ee20",
"md5": "0db580c9e2604a4f408a7f6459667239",
"sha256": "cce489113ea495a881341c814bd7edbec111c6019f1eb1626eaf2c91657d22dc"
},
"downloads": -1,
"filename": "partest-0.1.31.tar.gz",
"has_sig": false,
"md5_digest": "0db580c9e2604a4f408a7f6459667239",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.8",
"size": 13260,
"upload_time": "2024-12-09T09:38:14",
"upload_time_iso_8601": "2024-12-09T09:38:14.665855Z",
"url": "https://files.pythonhosted.org/packages/30/fb/e100c134d1fd54e179a1002ef765d66cc21dfaf1ab180e88f6181c62ee20/partest-0.1.31.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2024-12-09 09:38:14",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "Dec01",
"github_project": "partest",
"travis_ci": false,
"coveralls": false,
"github_actions": false,
"requirements": [
{
"name": "attrs",
"specs": [
[
"==",
"23.2.0"
]
]
},
{
"name": "certifi",
"specs": [
[
"==",
"2024.7.4"
]
]
},
{
"name": "charset-normalizer",
"specs": [
[
"==",
"2.0.12"
]
]
},
{
"name": "Faker",
"specs": [
[
">=",
"13.12.0"
]
]
},
{
"name": "idna",
"specs": [
[
"==",
"3.3"
]
]
},
{
"name": "iniconfig",
"specs": [
[
"==",
"1.1.1"
]
]
},
{
"name": "jsonschema",
"specs": [
[
"==",
"4.22.0"
]
]
},
{
"name": "packaging",
"specs": [
[
"==",
"21.3"
]
]
},
{
"name": "pluggy",
"specs": [
[
"==",
"1.5.0"
]
]
},
{
"name": "py",
"specs": [
[
"==",
"1.11.0"
]
]
},
{
"name": "pyparsing",
"specs": [
[
"==",
"3.0.9"
]
]
},
{
"name": "pyrsistent",
"specs": [
[
"==",
"0.18.1"
]
]
},
{
"name": "pytest",
"specs": [
[
"==",
"8.3.3"
]
]
},
{
"name": "python-dateutil",
"specs": [
[
"==",
"2.8.2"
]
]
},
{
"name": "requests",
"specs": [
[
"==",
"2.32.0"
]
]
},
{
"name": "six",
"specs": [
[
"==",
"1.16.0"
]
]
},
{
"name": "tomli",
"specs": [
[
"==",
"2.0.1"
]
]
},
{
"name": "urllib3",
"specs": [
[
"==",
"2.2.2"
]
]
},
{
"name": "pytest-repeat",
"specs": [
[
"==",
"0.9.1"
]
]
},
{
"name": "pytest-asyncio",
"specs": [
[
">=",
"0.23.7"
]
]
},
{
"name": "pydantic",
"specs": [
[
"==",
"2.7.1"
]
]
},
{
"name": "pytest-rerunfailures",
"specs": [
[
"~=",
"14.0"
]
]
},
{
"name": "ruff",
"specs": [
[
"==",
"0.4.2"
]
]
},
{
"name": "allure-pytest",
"specs": [
[
">=",
"2.8.18"
]
]
},
{
"name": "allure-python-commons",
"specs": [
[
"~=",
"2.13.5"
]
]
},
{
"name": "httpx",
"specs": [
[
"~=",
"0.27.2"
]
]
},
{
"name": "swagger-parser",
"specs": [
[
">=",
"1.0.2"
]
]
},
{
"name": "matplotlib",
"specs": [
[
">=",
"3.9.2"
]
]
},
{
"name": "pyyaml",
"specs": [
[
">=",
"6.0.2"
]
]
}
],
"lcname": "partest"
}