diff --git a/.github/workflows/poetry.yml b/.github/workflows/poetry.yml new file mode 100644 index 0000000..a7a2da2 --- /dev/null +++ b/.github/workflows/poetry.yml @@ -0,0 +1,54 @@ +on: + push: + branches: + - master + +jobs: + test: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + + - uses: actions/setup-python@v1 + with: + python-version: ^3.9 + + - name: Install dependencies + run: | + pip install --user poetry + + - name: Install dependencies + shell: bash + run: python -m poetry install + + - name: Run pytest + shell: bash + if: always() + run: python -m poetry run python -m pytest -v tests --alluredir=allure-results + + - name: Get Allure history + uses: actions/checkout@v2 + if: always() + continue-on-error: true + with: + ref: gh-pages + path: gh-pages + + - name: Allure Report action from marketplace + uses: simple-elf/allure-report-action@master + if: always() + id: allure-report + with: + allure_results: allure-results + gh_pages: gh-pages + allure_report: allure-report + allure_history: allure-history + + - name: Deploy report to Github Pages + if: always() + uses: peaceiris/actions-gh-pages@v2 + env: + PERSONAL_TOKEN: ${{ secrets.GITHUB_TOKEN }} + PUBLISH_BRANCH: gh-pages + PUBLISH_DIR: allure-history diff --git a/.gitignore b/.gitignore index ae61a53..aada650 100644 --- a/.gitignore +++ b/.gitignore @@ -107,3 +107,5 @@ venv.bak/ *.iml !.gitignore !.pre-commit-config.yaml + +.allure-results/ diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000..9599f3d --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,69 @@ +stages: + - testing # Tests run + - history_save # Save allure history + - reports # Report generation + - deploy # Report publication in gitlab pages + +docker_job: + stage: testing + tags: + - docker + image: python:3.9-alpine + before_script: + - pip install --user poetry + - python -m poetry install + script: + - python -m poetry run python -m pytest -v tests --alluredir=./allure-results tests/ + + allow_failure: true + artifacts: + when: always + paths: + - ./allure-results + expire_in: 1 day + +history_job: + stage: history_save + tags: + - docker + image: storytel/alpine-bash-curl + script: +# https://docs.gitlab.com/ee/ci/pipelines/job_artifacts.html#downloading-artifacts + - 'curl --location --output artifacts.zip "https://gitlab.com/api///-/jobs/artifacts/master/download?job=pages&job_token=$CI_JOB_TOKEN"' + - apk add unzip + - unzip artifacts.zip + - chmod -R 777 public + - cp -r ./public/history ./allure-results + allow_failure: true + artifacts: + paths: + - ./allure-results + expire_in: 1 day + rules: + - when: always + +allure_job: + stage: reports + tags: + - docker + image: frankescobar/allure-docker-service + script: + - allure generate -c ./allure-results -o ./allure-report + artifacts: + paths: + - ./allure-results + - ./allure-report + expire_in: 1 day + rules: + - when: always + +pages: + stage: deploy + script: + - mkdir public + - mv ./allure-report/* public + artifacts: + paths: + - public + rules: + - when: always diff --git a/README.md b/README.md index e24ab93..385b6f7 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,8 @@ +![.github/workflows/poetry.yml](https://github.com/77ripdrive/Python_sandbox/workflows/.github/workflows/poetry.yml/badge.svg) +[![PYTEST](https://img.shields.io/badge/pytest-v%206.2.1-green)](https://img.shields.io/badge/pytest-v%206.2.1-green) + +#### The test report can be found here [Allure report](https://77ripdrive.github.io/Python_sandbox.github.io/) + ## An example of initializing a Python project from scratch ### Dependency management with [Poetry-Python](https://python-poetry.org/docs/) @@ -6,7 +11,7 @@ pip3 install --user poetry ``` -#### for Windows with Powershell +#### for Windows with Powershell ```sh (Invoke-WebRequest -Uri https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py -UseBasicParsing).Content | python - @@ -36,3 +41,17 @@ or pre-commit sample-config > .pre-commit-config.yaml pre-commit run -a ``` + +#### To run tests, clone the project and run + +```sh +poetry install +poetry run pytest +``` + +#### To run marked tests with tag @api_service + +```sh +poetry run python -m pytest -k "api_service" + +``` diff --git a/asset/data_file.csv b/asset/data_file.csv deleted file mode 100644 index 7fce9cf..0000000 --- a/asset/data_file.csv +++ /dev/null @@ -1,3 +0,0 @@ -productId,productName,price,tags -2, apple, 25.6, green -3, banana, 30, grey diff --git a/asset/data_test_negative.csv b/asset/data_test_negative.csv new file mode 100644 index 0000000..6c38c9e --- /dev/null +++ b/asset/data_test_negative.csv @@ -0,0 +1,4 @@ +productId,productName,price,tags +2, apple, two, green +one, banana, 30, grey +4,one,20,green diff --git a/config.py b/config.py new file mode 100644 index 0000000..679ca7d --- /dev/null +++ b/config.py @@ -0,0 +1,2 @@ +BASE_URL_ZIPPOPO = "http://api.zippopotam.us" +DUCKDUCKGO_API = "https://api.duckduckgo.com/" diff --git a/poetry.lock b/poetry.lock index 6d8e694..8b7c8d6 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,3 +1,37 @@ +[[package]] +name = "allure-pytest" +version = "2.8.29" +description = "Allure pytest integration" +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +allure-python-commons = "2.8.29" +pytest = ">=4.5.0" +six = ">=1.9.0" + +[[package]] +name = "allure-python-commons" +version = "2.8.29" +description = "Common module for integrate allure with python-based frameworks" +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +attrs = ">=16.0.0" +pluggy = ">=0.4.0" +six = ">=1.9.0" + +[[package]] +name = "assertpy" +version = "1.1" +description = "Simple assertion library for unit testing in python with a fluent API" +category = "main" +optional = false +python-versions = "*" + [[package]] name = "atomicwrites" version = "1.4.0" @@ -20,6 +54,14 @@ docs = ["furo", "sphinx", "zope.interface"] tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface"] tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six"] +[[package]] +name = "cerberus" +version = "1.3.2" +description = "Lightweight, extensible schema and data validation tool for Python dictionaries." +category = "main" +optional = false +python-versions = ">=2.7" + [[package]] name = "certifi" version = "2020.12.5" @@ -224,6 +266,17 @@ py = "*" pytest = ">=4.3" six = ">=1.9.0" +[[package]] +name = "pytest-github-actions-annotate-failures" +version = "0.1.1" +description = "pytest plugin to annotate failed tests with a workflow command for GitHub Actions" +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +pytest = ">=4.0.0" + [[package]] name = "requests" version = "2.25.1" @@ -274,9 +327,20 @@ socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] [metadata] lock-version = "1.1" python-versions = "^3.9" -content-hash = "f0e56220cb63e32c7ad90922df3f89969a94692588873547a70f7cb16917287b" +content-hash = "0ec99c07802eefdfb1ce706b6bc2eaddb810f5482796f8fe858b52f150b6047d" [metadata.files] +allure-pytest = [ + {file = "allure-pytest-2.8.29.tar.gz", hash = "sha256:d37d235cd7c305e697fc90bd93f022f221d13c5a6184c1dec19f8f637092ad16"}, + {file = "allure_pytest-2.8.29-py3-none-any.whl", hash = "sha256:eb021f8610f55b2349a71e8b7d4dffcf08ddd363d44ac08482b628bc09bea5e9"}, +] +allure-python-commons = [ + {file = "allure-python-commons-2.8.29.tar.gz", hash = "sha256:b74c03e793bd4628d8c7644e49737c6ebdc1d883692da34d7547d1ef7ee7c863"}, + {file = "allure_python_commons-2.8.29-py3-none-any.whl", hash = "sha256:e9831bd01f25049a54cf47d7c17ad4b65506d5a35d70571cfaf8c4aa82a30f90"}, +] +assertpy = [ + {file = "assertpy-1.1.tar.gz", hash = "sha256:acc64329934ad71a3221de185517a43af33e373bb44dc05b5a9b174394ef4833"}, +] atomicwrites = [ {file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"}, {file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"}, @@ -285,6 +349,9 @@ attrs = [ {file = "attrs-20.3.0-py2.py3-none-any.whl", hash = "sha256:31b2eced602aa8423c2aea9c76a724617ed67cf9513173fd3a4f03e3a929c7e6"}, {file = "attrs-20.3.0.tar.gz", hash = "sha256:832aa3cde19744e49938b91fea06d69ecb9e649c93ba974535d08ad92164f700"}, ] +cerberus = [ + {file = "Cerberus-1.3.2.tar.gz", hash = "sha256:302e6694f206dd85cb63f13fd5025b31ab6d38c99c50c6d769f8fa0b0f299589"}, +] certifi = [ {file = "certifi-2020.12.5-py2.py3-none-any.whl", hash = "sha256:719a74fb9e33b9bd44cc7f3a8d94bc35e4049deebe19ba7d8e108280cfd59830"}, {file = "certifi-2020.12.5.tar.gz", hash = "sha256:1a4995114262bffbc2413b159f2a1a480c969de6e6eb13ee966d470af86af59c"}, @@ -388,6 +455,10 @@ pytest-bdd = [ {file = "pytest-bdd-4.0.2.tar.gz", hash = "sha256:982489f2f036c7561affe4eeb5b392a37e1ace2a9f260cad747b1c8119e63cfd"}, {file = "pytest_bdd-4.0.2-py2.py3-none-any.whl", hash = "sha256:74ea5a147ea558c99ae83d838e6acbe5c9e6843884a958f8231615d96838733d"}, ] +pytest-github-actions-annotate-failures = [ + {file = "pytest-github-actions-annotate-failures-0.1.1.tar.gz", hash = "sha256:66445649ac8c0e1a05475faeda1679f0609a77861f9c620f68429cf6daf4ddc2"}, + {file = "pytest_github_actions_annotate_failures-0.1.1-py2.py3-none-any.whl", hash = "sha256:e997068fff4909331c3b5f4e2c7868535aa4c62cb71b96b9d9724733f80bba26"}, +] requests = [ {file = "requests-2.25.1-py2.py3-none-any.whl", hash = "sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e"}, {file = "requests-2.25.1.tar.gz", hash = "sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804"}, diff --git a/pyproject.toml b/pyproject.toml index 4204d3d..9deddd3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,9 +11,26 @@ requests = "^2.25.1" jsonschema = "^3.2.0" pytest-bdd = "^4.0.2" jsonpath = "^0.82" +allure-pytest = "^2.8.29" +pytest-github-actions-annotate-failures = "^0.1.1" +Cerberus = "^1.3.2" +assertpy = "^1.1" [tool.poetry.dev-dependencies] [build-system] requires = ["poetry-core>=1.0.0"] build-backend = "poetry.core.masonry.api" + +[tool.pytest.ini_options] +markers = [ + "api_service: marks tests for api testing')", + "jsonValidation: marks tests with jsonschema validation", + "duck's_search: marks tests with duckducks api search", +] +filterwarnings = [ + "error", + "ignore::UserWarning", + # note the use of single quote below to denote "raw" strings in TOML + 'ignore:function ham\(\) is deprecated:DeprecationWarning', +] diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000..2584ed2 --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,21 @@ +import pytest + +from tests.utils.utils import read_data_from_json_file + + +# bdd hooks +def pytest_bdd_step_error(request, feature, scenario, step, step_func, step_func_args, exception): + print(f"Step failed: {step}") + print(f"Request failed: {request}") + + +@pytest.fixture(scope="session") +def set_base_payload(): + payload = {"productId": 1, "productName": "A green door", "productPrice": 12.50, "tags": ["home", "green"]} + yield payload + + +@pytest.fixture(scope="session") +def set_schema(): + schema = read_data_from_json_file("schema_first.json") + yield schema diff --git a/tests/features/api.feature b/tests/features/api.feature index fb2a633..3f47506 100644 --- a/tests/features/api.feature +++ b/tests/features/api.feature @@ -1,3 +1,4 @@ +@pytest.mark.duckduck_search Feature: DuckDuckGo Instant Answer API As an application developer, I want to get instant answers for search terms via a REST API, diff --git a/tests/features/cucumbers.feature b/tests/features/cucumbers.feature index 8fbc188..653fa9d 100644 --- a/tests/features/cucumbers.feature +++ b/tests/features/cucumbers.feature @@ -13,7 +13,7 @@ Feature: Cucumber Basket | initial | some | total | | 0 | 3 | 3 | | 3 | 4 | 7 | - | 7 | 5 | 12 | + | 2 | 1 | 3 | Scenario Outline: Remove cucumbers from the basket diff --git a/tests/features/zippopo.feature b/tests/features/zippopo.feature index 06190d6..04fc443 100644 --- a/tests/features/zippopo.feature +++ b/tests/features/zippopo.feature @@ -1,3 +1,4 @@ +@pytest.mark.api_service Feature: Zippopo Instant Answer API As an application developer, I want to get instant answers for search terms via a REST API, @@ -10,6 +11,6 @@ Feature: Zippopo Instant Answer API Then the response contains results for "" Examples: - | country_code | zip_code | place_name | - | us | 12345 | Schenectady | - | nl | 3825 | Vathorst | + | country_code | zip_code | place_name | + | us | 12345 | Schenectady | + | nl | 3825 | Vathorst | diff --git a/tests/models_steps/__init__.py b/tests/models/__init__.py similarity index 100% rename from tests/models_steps/__init__.py rename to tests/models/__init__.py diff --git a/tests/models_steps/cucumbers.py b/tests/models/cucumbers.py similarity index 99% rename from tests/models_steps/cucumbers.py rename to tests/models/cucumbers.py index 26dbcb3..28bdec3 100644 --- a/tests/models_steps/cucumbers.py +++ b/tests/models/cucumbers.py @@ -1,5 +1,4 @@ class CucumberBasket: - def __init__(self, initial_count=0, max_count=10): if initial_count < 0: raise ValueError("Initial cucumber basket count must not be negative") diff --git a/tests/steps_defs/conftest.py b/tests/steps_defs/conftest.py deleted file mode 100644 index f948701..0000000 --- a/tests/steps_defs/conftest.py +++ /dev/null @@ -1,6 +0,0 @@ -# Hooks - - -def pytest_bdd_step_error(request, feature, scenario, step, step_func, step_func_args, exception): - print(f"Step failed: {step}") - print(f"Request failed: {request}") diff --git a/tests/steps_defs/test_api_steps.py b/tests/steps_defs/test_api_steps.py index 58f8541..50ffccc 100644 --- a/tests/steps_defs/test_api_steps.py +++ b/tests/steps_defs/test_api_steps.py @@ -5,11 +5,11 @@ from pytest_bdd import then # Shared Variables - -DUCKDUCKGO_API = "https://api.duckduckgo.com/" +from config import DUCKDUCKGO_API # Scenarios + scenarios("../features/api.feature", example_converters=dict(phrase=str)) @@ -19,7 +19,7 @@ @given('the DuckDuckGo API is queried with ""', target_fixture="ddg_response") def ddg_response(phrase): params = {"q": phrase, "format": "json"} - response = requests.get(DUCKDUCKGO_API, params=params) + response = requests.get(f"{DUCKDUCKGO_API}", params=params) return response diff --git a/tests/steps_defs/test_cucumber_steps.py b/tests/steps_defs/test_cucumber_steps.py index b68db8e..4432356 100644 --- a/tests/steps_defs/test_cucumber_steps.py +++ b/tests/steps_defs/test_cucumber_steps.py @@ -1,22 +1,26 @@ -from pytest_bdd import scenarios, parsers, given, when, then +from pytest_bdd import given +from pytest_bdd import parsers +from pytest_bdd import scenarios +from pytest_bdd import then +from pytest_bdd import when -from tests.models_steps.cucumbers import CucumberBasket +from tests.models.cucumbers import CucumberBasket EXTRA_TYPES = { - 'Number': int, + "Number": int, } CONVERTERS = { - 'initial': int, - 'some': int, - 'total': int, + "initial": int, + "some": int, + "total": int, } -scenarios('../features/cucumbers.feature', example_converters=CONVERTERS) +scenarios("../features/cucumbers.feature", example_converters=CONVERTERS) -@given(parsers.cfparse('the basket has "{initial:Number}" cucumbers', extra_types=EXTRA_TYPES), target_fixture='basket') -@given('the basket has "" cucumbers', target_fixture='basket') +@given(parsers.cfparse('the basket has "{initial:Number}" cucumbers', extra_types=EXTRA_TYPES), target_fixture="basket") +@given('the basket has "" cucumbers', target_fixture="basket") def basket(initial): return CucumberBasket(initial_count=initial) diff --git a/tests/steps_defs/test_zippo_steps.py b/tests/steps_defs/test_zippo_steps.py index e3372a0..4df1e01 100644 --- a/tests/steps_defs/test_zippo_steps.py +++ b/tests/steps_defs/test_zippo_steps.py @@ -5,6 +5,8 @@ from pytest_bdd import scenarios from pytest_bdd import then +from config import BASE_URL_ZIPPOPO + CONVERTERS = {"country_code": str, "zip_code": int, "place_name": str} scenarios("../features/zippopo.feature", example_converters=CONVERTERS) @@ -12,8 +14,7 @@ @given('the Zippopotam API is queried with "" and ""', target_fixture="ddg_response") def ddg_response(country_code, zip_code): - url = "http://api.zippopotam.us/{}/{}".format(country_code, zip_code) - response = requests.get(url) + response = requests.get(f"{BASE_URL_ZIPPOPO}/{country_code}/{zip_code}") return response diff --git a/tests/test_base.py b/tests/test_base.py deleted file mode 100644 index 1f3eb81..0000000 --- a/tests/test_base.py +++ /dev/null @@ -1,2 +0,0 @@ -def test_first_test(): - assert str(1 + 1) == '2' diff --git a/tests/test_cerberus_assertpy.py b/tests/test_cerberus_assertpy.py new file mode 100644 index 0000000..315555b --- /dev/null +++ b/tests/test_cerberus_assertpy.py @@ -0,0 +1,35 @@ +from dataclasses import dataclass + +import pytest +from assertpy import assert_that +from cerberus import Validator + + +@dataclass +class Person: + name: str + age: int + + +class PersonValidator(Validator): + def validate_person(self, obj): + return self.validate(obj.__dict__) + + +schema = {"name": {"type": "string", "minlength": 2}, "age": {"type": "integer", "min": 18, "max": 65}} + + +@pytest.mark.json_validation_cerberus +def test_cerberus_validator_base(set_schema, set_base_payload): + v = PersonValidator(schema) + person = Person("John Doe", 20) + result = v.validate_person(person) + assert_that(result, description=v.errors).is_true() + + +@pytest.mark.json_validation_cerberus +def test_cerberus_validator_negative(set_schema, set_base_payload): + v = PersonValidator(schema) + children = Person("Zoe Doe", 2) + result = v.validate_person(children) + assert_that(result, description=v.errors).is_false() diff --git a/tests/test_json_ex_b.py b/tests/test_json_ex_b.py deleted file mode 100644 index 84e00b9..0000000 --- a/tests/test_json_ex_b.py +++ /dev/null @@ -1,22 +0,0 @@ -import json - -schema = { - "id": "int", - "username": "string", - "firstName": "string", - "lastName": "string", - "email": "string", - "password": "string", - "phone": "string", - "userStatus": "int" -} - - -def builder_for_json(dict): - items = sorted(dict.items(), key=lambda item: item[0]) - values = [item[1] for item in items] - json_dict = json.dumps(values) - return json_dict - - -print(builder_for_json(schema)) diff --git a/tests/test_json_schema_base.py b/tests/test_json_schema_base.py index 4841224..e48a045 100644 --- a/tests/test_json_schema_base.py +++ b/tests/test_json_schema_base.py @@ -1,49 +1,12 @@ -import csv -import json - +import allure import pytest from jsonschema import Draft7Validator -from jsonschema import validate - -path_to_test_data_csv_file = "../asset/data_file.csv" -path_json_schema_file = "../asset/schema_first.json" - - -def read_test_data_from_csv(path): - test_data = [] - with open(path, newline="") as csvfile: - data = csv.reader(csvfile, delimiter=",") - next(data) - for row in data: - test_data.append(row) - return test_data - - -def get_data_from_json_schema(path): - with open(path, "r") as schema_file: - return json.load(schema_file) - - -payload = {"productId": 1, "productName": "A green door", "productPrice": 12.50, "tags": ["home", "green"]} - - -def test_using_csv_with_different_fields(): - with open(path_json_schema_file, "r") as schema_file: - schema = json.load(schema_file) - result = Draft7Validator(schema).is_valid(payload) - print(result) - assert result == True -# @pytest.mark.skip -@pytest.mark.parametrize("productId,productName,price,tags", read_test_data_from_csv(path_to_test_data_csv_file)) -def test_using_csv_with_different_fields_negative(productId, productName, price, tags): - schema = get_data_from_json_schema(path_json_schema_file) - payload.update({"productId": productId}) - payload.update({"productName": productName}) - payload.update({"productPrice": price}) - payload.update({"tags": tags}) - try: - validate(payload, schema) - except: - print(f" - Validation error") +@allure.feature("Jsonschema validation") +@allure.story("Validation with correct payload value") +@pytest.mark.jsonValidation +def test_using_csv_with_different_fields(set_schema, set_base_payload): + with allure.step("Validation with correct JsonSchema"): + result = Draft7Validator(set_schema).is_valid(set_base_payload) + assert result == True diff --git a/tests/test_json_schema_negative.py b/tests/test_json_schema_negative.py new file mode 100644 index 0000000..3ac85dd --- /dev/null +++ b/tests/test_json_schema_negative.py @@ -0,0 +1,38 @@ +import json +import logging +import os + +import allure +import pytest +from jsonschema import ValidationError +from jsonschema import validate + +LOGGER = logging.getLogger(__name__) +path_json_schema_file = os.getcwd() + "/asset/schema_first.json" + + +def get_data_from_json_schema(path): + with open(path, "r") as schema_file: + return json.load(schema_file) + + +@allure.feature("Jsonschema validation") +@allure.story("Validation with incorrect value") +@pytest.mark.jsonValidation +@pytest.mark.parametrize( + "productId,productName,price,tags", + [(2, "apple", "two", ["green", "grey"]), ("one", "banana", 30, []), (4, "one", 20, "green")], +) +def test_using_csv_with_different_fields_negative(productId, productName, price, tags): + payload = {"productId": None, "productName": None, "productPrice": None, "tags": None} + with allure.step("insert nit correct value "): + payload.update({"productId": productId}) + payload.update({"productName": productName}) + payload.update({"productPrice": price}) + payload.update({"tags": tags}) + schema = get_data_from_json_schema(path_json_schema_file) + with allure.step("Validation with base JsonSchema"): + with pytest.raises(ValidationError) as e: + validate(payload, schema=schema) + assert e.value.__module__.strip("ValidationError") == "jsonschema.exceptions" + LOGGER.info(e.value) diff --git a/tests/test_ex_a.py b/tests/test_tuple_as_dict.py similarity index 52% rename from tests/test_ex_a.py rename to tests/test_tuple_as_dict.py index c832838..7888836 100644 --- a/tests/test_ex_a.py +++ b/tests/test_tuple_as_dict.py @@ -1,23 +1,20 @@ from collections import namedtuple -Task = namedtuple('Task', ['summary', 'owner', 'done', 'id']) +Task = namedtuple("Task", ["summary", "owner", "done", "id"]) Task.__new__.__defaults__ = (None, None, False, None) def test_asdict(): """_asdict() should return a dictionary.""" - t_task = Task('do something', 'Todos', True, 22) + t_task = Task("do something", "Todos", True, 22) t_dict = t_task._asdict() - expected = {'summary': 'do something', - 'owner': 'Todos', - 'done': True, - 'id': 22} + expected = {"summary": "do something", "owner": "Todos", "done": True, "id": 22} assert t_dict == expected def test_replace(): """replace() should change passed in fields.""" - t_before = Task('finish book', 'Ioan', False, 20) + t_before = Task("finish book", "Ioan", False, 20) t_after = t_before._replace(id=10, done=True) - t_expected = Task('finish book', 'Ioan', True, 10) + t_expected = Task("finish book", "Ioan", True, 10) assert t_after == t_expected diff --git a/tests/utils/__init__.py b/tests/utils/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/utils/utils.py b/tests/utils/utils.py new file mode 100644 index 0000000..00fb96e --- /dev/null +++ b/tests/utils/utils.py @@ -0,0 +1,67 @@ +import csv +import json +import os +from pathlib import Path + +import jsonpath as jsonpath + +BASE_PATH = Path.cwd().joinpath("asset") +test_data = [] + + +def read_data_from_json_file(file_name): + path = get_path(file_name) + with path.open(mode="r") as schema_file: + return json.load(schema_file) + + +def read_data_from_csv_file(file_name): + + path = get_path(file_name) + with path.open(mode="r") as csvfile: + data = csv.reader(csvfile, delimiter=",") + next(data) + for row in data: + test_data.append(row) + return test_data + + +def get_path(file_name): + if ".json" in file_name: + path = BASE_PATH.joinpath(file_name) + elif ".csv" in file_name: + path = BASE_PATH.joinpath(file_name) + return path + + +def read_data(file): + """Чтение csv файла в list of jsons""" + with open(f'{file}', "r", encoding='utf-8') as f: + dict_reader = csv.DictReader(f, delimiter=";") + for row in dict_reader: + dict_from_csv = dict(row) + test_data.append(dict_from_csv) + + return test_data + + +def write_scv_to_list_dict_file(file, name): + """ Чтение данных из csv в корне проекта и запись в файл как list of jsons """ + dicts = read_data(file) + file_name = os.getcwd() + f"/assets/assets_{name}.py" + with open(file_name, "a+", encoding="utf-8") as f: + json.dump(dicts, f, ensure_ascii=False, sort_keys=True, indent=4) + + +def search_data_from_json_to_list_of_jsons(base_file: json, jsonpath_expression: str): + """метод для получения list required полей из openApi + в зависимости от endpoint запроса и статуса response""" + list_jsons = jsonpath.jsonpath(base_file, jsonpath_expression) + return list_jsons + + +def write_json_file(base_name, json_object: json): + """Too do """ + file_name = os.getcwd() + f"/assets/{base_name}.json" + with open(file_name, "a+", encoding="utf-8") as f: + json.dump(json_object, f, ensure_ascii=False, sort_keys=True, indent=4) \ No newline at end of file