{"id":21582686,"url":"https://github.com/automation-test-starter/pytest-api-test-starter","last_synced_at":"2026-03-15T11:40:39.868Z","repository":{"id":206893281,"uuid":"716845540","full_name":"Automation-Test-Starter/Pytest-API-Test-Starter","owner":"Automation-Test-Starter","description":"An introductory document on using pytest for API testing.","archived":false,"fork":false,"pushed_at":"2023-11-20T06:56:48.000Z","size":106,"stargazers_count":33,"open_issues_count":0,"forks_count":10,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-05-01T12:39:43.321Z","etag":null,"topics":["allure","api-automation-testing","pytest","python","python3","quickstart"],"latest_commit_sha":null,"homepage":"","language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Automation-Test-Starter.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2023-11-10T01:54:33.000Z","updated_at":"2025-04-23T04:17:26.000Z","dependencies_parsed_at":"2023-11-18T14:58:40.434Z","dependency_job_id":"090042aa-0401-4355-9ccd-4f3c88040ae6","html_url":"https://github.com/Automation-Test-Starter/Pytest-API-Test-Starter","commit_stats":null,"previous_names":["automation-test-starter/pytest-api-test-starter"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/Automation-Test-Starter/Pytest-API-Test-Starter","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Automation-Test-Starter%2FPytest-API-Test-Starter","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Automation-Test-Starter%2FPytest-API-Test-Starter/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Automation-Test-Starter%2FPytest-API-Test-Starter/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Automation-Test-Starter%2FPytest-API-Test-Starter/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Automation-Test-Starter","download_url":"https://codeload.github.com/Automation-Test-Starter/Pytest-API-Test-Starter/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Automation-Test-Starter%2FPytest-API-Test-Starter/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":267858891,"owners_count":24155985,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-07-30T02:00:09.044Z","response_time":70,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["allure","api-automation-testing","pytest","python","python3","quickstart"],"created_at":"2024-11-24T14:16:34.331Z","updated_at":"2026-03-15T11:40:39.835Z","avatar_url":"https://github.com/Automation-Test-Starter.png","language":null,"funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003c!-- markdownlint-disable MD041 --\u003e\n\u003c!-- markdownlint-disable MD033 --\u003e\n\u003cdiv align=\"right\"\u003e\u003cstrong\u003e\u003ca href=\"./README_ZH.md\"\u003e🇨🇳中文\u003c/a\u003e\u003c/strong\u003e  | \u003cstrong\u003e🇬🇧English\u003c/strong\u003e\u003c/div\u003e\n\u003c!-- markdownlint-disable MD041 --\u003e\n\u003c!-- markdownlint-disable MD033 --\u003e\n\n# Pytest API Automation Testing QuickStart Project\n\nAn introductory QuickStart project document on API automation testing with Pytest.\n\n- [Pytest API Automation Testing QuickStart Project](#pytest-api-automation-testing-quickstart-project)\n  - [Introduction](#introduction)\n    - [Introducing Pytest](#introducing-pytest)\n    - [Introduction to python virtual environments](#introduction-to-python-virtual-environments)\n  - [Project dependencies](#project-dependencies)\n  - [Project directory structure](#project-directory-structure)\n  - [Build a Pytest API Automation Test Project from 0 to 1](#build-a-pytest-api-automation-test-project-from-0-to-1)\n    - [1. Create a project directory](#1-create-a-project-directory)\n    - [2.Project initialization](#2project-initialization)\n    - [3.Install project dependencies](#3install-project-dependencies)\n    - [4. Create new test files and test cases](#4-create-new-test-files-and-test-cases)\n    - [5. Writing Test Cases](#5-writing-test-cases)\n    - [6.Run test cases](#6run-test-cases)\n    - [7.View test report](#7view-test-report)\n    - [8.Integration pytest-html-reporter test report](#8integration-pytest-html-reporter-test-report)\n      - [Install pytest-html-reporter dependency](#install-pytest-html-reporter-dependency)\n      - [Configuring Test Report Parameters](#configuring-test-report-parameters)\n      - [Run test cases](#run-test-cases)\n      - [Viewing the test report](#viewing-the-test-report)\n  - [Advanced Usage](#advanced-usage)\n    - [CI/CD integration](#cicd-integration)\n      - [Integration github action](#integration-github-action)\n    - [Common Assertions](#common-assertions)\n    - [Data-driven](#data-driven)\n      - [Create the test configuration file](#create-the-test-configuration-file)\n      - [Writing Test Configuration Files](#writing-test-configuration-files)\n      - [Create the test data file](#create-the-test-data-file)\n      - [Writing test data files](#writing-test-data-files)\n      - [Updating test cases to support data driving](#updating-test-cases-to-support-data-driving)\n      - [Run the test case to confirm the data driver is working](#run-the-test-case-to-confirm-the-data-driver-is-working)\n    - [Multi-environment support](#multi-environment-support)\n      - [New test configuration files for different environments](#new-test-configuration-files-for-different-environments)\n      - [Writing different environment test profiles](#writing-different-environment-test-profiles)\n      - [New Different Environment Test Data File](#new-different-environment-test-data-file)\n      - [Writing test data files for different environments](#writing-test-data-files-for-different-environments)\n      - [Configure fixture to support multiple environments](#configure-fixture-to-support-multiple-environments)\n      - [Update test case to support multi environment](#update-test-case-to-support-multi-environment)\n      - [Run this test case to confirm that multi-environment support is in effect](#run-this-test-case-to-confirm-that-multi-environment-support-is-in-effect)\n    - [Integration with allure reporting](#integration-with-allure-reporting)\n      - [Install allure-pytest library](#install-allure-pytest-library)\n      - [Configuration allure-pytest library](#configuration-allure-pytest-library)\n      - [Adjusting test cases to support allure reporting](#adjusting-test-cases-to-support-allure-reporting)\n      - [Run test cases to generate allure reports](#run-test-cases-to-generate-allure-reports)\n      - [View allure report](#view-allure-report)\n      - [Adapting CI/CD processes to support allure reporting](#adapting-cicd-processes-to-support-allure-reporting)\n      - [View github action allure report](#view-github-action-allure-report)\n    - [concurrent testing and distributed testing](#concurrent-testing-and-distributed-testing)\n      - [`pytest-xdist` Feature Introduction](#pytest-xdist-feature-introduction)\n      - [Installing `pytest-xdist` dependency](#installing-pytest-xdist-dependency)\n      - [Example of running a test case concurrently](#example-of-running-a-test-case-concurrently)\n        - [Execute test cases concurrently with 3 workers](#execute-test-cases-concurrently-with-3-workers)\n        - [concurrently executes the test cases with 3 workers, and each worker prints the progress of the test cases](#concurrently-executes-the-test-cases-with-3-workers-and-each-worker-prints-the-progress-of-the-test-cases)\n      - [Distributed testing example](#distributed-testing-example)\n        - [Distributed test where each node runs a set of tests](#distributed-test-where-each-node-runs-a-set-of-tests)\n        - [Distributed testing, where each node runs a set of tests and each worker prints the progress of the test cases](#distributed-testing-where-each-node-runs-a-set-of-tests-and-each-worker-prints-the-progress-of-the-test-cases)\n        - [Distributed testing, each node runs a set of tests, and each worker prints the progress of the test cases, as well as the output of the test logs](#distributed-testing-each-node-runs-a-set-of-tests-and-each-worker-prints-the-progress-of-the-test-cases-as-well-as-the-output-of-the-test-logs)\n    - [Filtering test case execution](#filtering-test-case-execution)\n      - [Defining Pytest Markers](#defining-pytest-markers)\n      - [Marking Test Cases](#marking-test-cases)\n      - [Filtering Test Case Execution](#filtering-test-case-execution-1)\n\n## Introduction\n\n### Introducing Pytest\n\nPytest is a popular Python testing framework for writing, organizing, and running various types of automated tests. It provides a rich set of features that make it easy to write and manage test cases, as well as generate detailed test reports. Here are some of the key features and benefits of Pytest:\n\n1. **Simple and easy to use**: Pytest is designed to make writing test cases simple and easy to understand. You can write test assertions using Python's standard `assert` statement without having to learn a new assertion syntax.\n\n2. **Automatic Discovery of Test Cases**: Pytest can automatically discover and run test cases in your project without explicitly configuring the test suite. Test case files can be named `test_*.py` or `*_test.py`, or use a specific test function naming convention.\n\n3. **Rich plugin ecosystem**: Pytest can be extended with plugins. There are many third-party plug-ins available to meet different testing needs, such as Allure reporting, parameterization, coverage analysis, and so on.\n\n4. **Parameterized Testing**: Pytest supports parameterized testing, which allows you to run the same test case multiple times, but with different parameters. This reduces code duplication and improves test coverage.\n\n5. **Exception and fault localization**: Pytest provides detailed error and exception information that helps you locate and resolve problems more easily. It also provides detailed traceback information.\n\n6. **Parallel Test Execution**: Pytest supports parallel execution of test cases, which increases the speed of test execution, especially in large projects.\n\n7. **Multiple Report Formats**: Pytest supports multiple test report formats, including terminal output, JUnit XML, HTML reports and Allure reports. These reports can help you visualize test results.\n\n8. **Command Line Options**: Pytest provides a rich set of command line options to customize the behavior of test runs, including filtering, retrying, coverage analysis, and more.\n\n9. **Integration**: Pytest can be easily integrated with other testing frameworks and tools (e.g. Selenium, Django, Flask, etc.) as well as continuous integration systems (e.g. Jenkins, Travis CI, etc.).\n\n10. **Active Community**: Pytest has an active community with extensive documentation and tutorials for learning and reference. You can also get support and solve problems in the community.\n\nIn short, Pytest is a powerful and flexible testing framework for projects of all sizes and types. Its ease of use, automation capabilities, and rich set of plugins make it one of the go-to tools in Python testing.\n\nOfficial website: [https://docs.pytest.org/en/latest/](https://docs.pytest.org/en/latest/)\n\n### Introduction to python virtual environments\n\nA Python virtual environment is a mechanism for creating and managing multiple isolated development environments within a single Python installation. Virtual environments help resolve dependency conflicts between different projects by ensuring that each project can use its own independent Python packages and libraries without interfering with each other. Here are the steps on how to create and use a Python virtual environment:\n\n1. **Install the Virtual Environment Tool**.\n   Before you begin, make sure you have installed Python's virtual environment tools. In Python 3.3 and later, the `venv` module is built-in and can be used to create virtual environments. If you're using an older version of Python, you can install the `virtualenv` tool.\n\n   For Python 3.3+, the `venv` tool is built-in and does not require additional installation.\n\n   For Python 2.x, you can install the `virtualenv` tool with the following command:\n\n   ```bash\n   pip install virtualenv\n   ```\n\n2. **Creating a virtual environment**.\n   Open a terminal, move to the directory where you wish to create the virtual environment, and run the following command to create the virtual environment:\n\n   Use `venv` (for Python 3.3+):\n\n   ```bash\n   python -m venv myenv\n   ```\n\n   Use `virtualenv` (for Python 2.x):\n\n   ```bash\n   virtualenv myenv\n   ```\n\n   In the above command, `myenv` is the name of the virtual environment and you can customize the name.\n\n3. **Activate virtual environment**.\n   To start using the virtual environment, you need to activate it. The activation command is slightly different for different operating systems:\n\n   - on macOS and Linux:\n\n    ```bash\n     source myenv/bin/activate\n    ```\n\n   - On Windows (using Command Prompt):\n\n    ```bash\n     myenv\\Scripts\\activate\n    ```\n\n   - On Windows (using PowerShell):\n\n    ```bash\n     .\\myenv\\Scripts\\Activate.ps1\n    ```\n\n   Once the virtual environment is activated, you will see the name of the virtual environment in front of the terminal prompt, indicating that you are in the virtual environment.\n\n4. **Installing dependencies in a virtual environment**.\n   In a virtual environment, you can use `pip` to install any Python packages and libraries required by your project, and these dependencies will be associated with that virtual environment. Example:\n\n   ```bash\n   pip install requests\n   ```\n\n5. **Using a virtual environment**.\n   When working in a virtual environment, you can run Python scripts and use packages installed in the virtual environment. This ensures that your project runs in a separate environment and does not conflict with the global Python installation.\n\n6. **Exiting the virtual environment**.\n   To exit the virtual environment, simply run the following command in a terminal:\n\n   ```bash\n   deactivate\n   ```\n\n   This returns you to the global Python environment.\n\nBy using a virtual environment, you can maintain clean dependencies between projects and ensure project stability and isolation. This is a good practice in Python development.\n\n## Project dependencies\n\n\u003e The following environments need to be installed in advance\n\n- [x] python, demo 版本为 v3.11.6\n\n\u003e Just install python 3.x or higher.\n\n## Project directory structure\n\nThe following is an example of the directory structure of a Pytest interface automation test project:\n\n\u003e Subsequent demo projects will introduce allure reports, so there will be an additional allure-report directory.\n\n```text\nPytest-allure-demo/\n    ├── tests/                  # test case files\n    │   ├── test_login.py       # Example test case file\n    │   ├── test_order.py       # Example test case file\n    │   └── ...\n    ├── data/                   # test data files (e.g. JSON, CSV, etc.)\n    │   ├── dev_test_data.json      #  Test data file for development environment.\n    │   ├── prod_test_data.json      #  Test data file for prod environment.\n    │   ├── ...\n    ├── config/\n    │   ├── dev_config.json  # Development environment configuration file\n    │   ├── prod_config.json  # Production environment configuration file\n    │   ├── ...\n    ├── conftest.py             # Pytest's global configuration file\n    ├── pytest.ini              # Pytest configuration file\n    ├── requirements.txt        # Project dependencies file\n    └── allure-report/          # Allure reports\n```\n\n## Build a Pytest API Automation Test Project from 0 to 1\n\n### 1. Create a project directory\n\n```shell\nmkdir Pytest-API-Testing-Demo\n```\n\n### 2.Project initialization\n\n```shell\n// Go to the project folder\ncd Pytest-API-Testing-Demo\n// Create the project python project virtual environment\npython -m venv .env\n// Enable the project python project virtual environment\nsource .env/bin/activate\n```\n\n### 3.Install project dependencies\n\n```shell\n// Install the requests package\npip install requests\n// Install the pytest package\npip install pytest\n// Save the project dependencies to the requirements.txt file.\npip freeze \u003e requirements.txt\n```\n\n### 4. Create new test files and test cases\n\n```shell\n// Create a new tests folder\nmkdir tests\n// Create a new test case file\ncd tests\ntouch test_demo.py\n```\n\n### 5. Writing Test Cases\n\n\u003e The test API can be referred to the demoAPI.md file in the project.\n\n```python\nimport requests\n\n\nclass TestPytestDemo:\n\n    def test_get_demo(self):\n        base_url = \"https://jsonplaceholder.typicode.com\"\n        # SEND REQUEST\n        response = requests.get(f\"{base_url}/posts/1\")\n        # ASSERT\n        assert response.status_code == 200\n        assert response.json()['userId'] == 1\n        assert response.json()['id'] == 1\n\n    def test_post_demo(self):\n        base_url = \"https://jsonplaceholder.typicode.com\"\n        requests_data = {\n            \"title\": \"foo\",\n            \"body\": \"bar\",\n            \"userId\": 1\n        }\n        # SEND REQUEST\n        response = requests.post(f\"{base_url}/posts\", requests_data)\n        # ASSERT\n        assert response.status_code == 201\n        print(response.json())\n        assert response.json()['userId'] == '1'\n        assert response.json()['id'] == 101\n```\n\n### 6.Run test cases\n\n```shell\npytest\n```\n\n### 7.View test report\n\n![CsoB4y](https://cdn.jsdelivr.net/gh/naodeng/blogimg@master/uPic/CsoB4y.png)\n\n### 8.Integration pytest-html-reporter test report\n\n\u003e \u003chttps://github.com/prashanth-sams/pytest-html-reporter\u003e\n\n#### Install pytest-html-reporter dependency\n\n```shell\npip install pytest-html-reporter \n```\n\n#### Configuring Test Report Parameters\n\n- Create a new pytest.ini file in the project root directory.\n- Add the following\n\n```ini\n[pytest]\naddopts = -vs -rf --html-report=./report --title='PYTEST REPORT' --self-contained-html\n```\n\n#### Run test cases\n\n```shell\npytest\n```\n\n#### Viewing the test report\n\nThe report is located in the report directory in the project root directory, use your browser to open the pytest_html_report.html file to view it.\n\n![8JdxbA](https://cdn.jsdelivr.net/gh/naodeng/blogimg@master/uPic/8JdxbA.png)\n\n## Advanced Usage\n\n### CI/CD integration\n\n#### Integration github action\n\nUse github action as an example, and other CI tools similarly\n\nSee the demo at \u003chttps://github.com/Automation-Test-Starter/Pytest-API-Test-Demo\u003e\n\n- Create the .github/workflows directory: In your GitHub repository, create a directory called .github/workflows. This will be where the GitHub Actions workflow files will be stored.\n\n- Create a workflow file: Create a YAML-formatted workflow file, such as pytest.yml, in the .github/workflows directory.\n\n- Edit the pytest.yml file: Copy the following into the file\n  \n```yaml\n# This workflow will install Python dependencies, run tests and lint with a single version of Python\n# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python\n\nname: Pytest API Testing\n\non:\n  push:\n    branches: [ \"main\" ]\n  pull_request:\n    branches: [ \"main\" ]\n\npermissions:\n  contents: read\n\njobs:\n  Pytes-API-Testing:\n\n    runs-on: ubuntu-latest\n\n    steps:\n    - uses: actions/checkout@v3\n    - name: Set up Python 3.10\n      uses: actions/setup-python@v3\n      with:\n        python-version: \"3.10\"\n    - name: Install dependencies\n      run: |\n        python -m pip install --upgrade pip\n        pip install -r requirements.txt\n        \n    - name: Test with pytest\n      run: |\n        pytest\n\n    - name: Archive Pytest test report\n      uses: actions/upload-artifact@v3\n      with:\n        name: SuperTest-test-report\n        path: report\n          \n    - name: Upload Pytest report to GitHub\n      uses: actions/upload-artifact@v3\n      with:\n        name: Pytest-test-report\n        path: report\n```\n\n- Commit the code: Add the pytest.yml file to your repository and commit.\n- View test reports: In GitHub, navigate to your repository. Click the Actions tab at the top and then click the Pytest API Testing workflow on the left. You should see the workflow running, wait for the execution to complete and you can view the results.\n\n![yE65LO](https://cdn.jsdelivr.net/gh/naodeng/blogimg@master/uPic/yE65LO.png)\n\n### Common Assertions\n\nUsing Pytest During the writing of interface automation test cases, we need to use various assertions to verify the expected results of the tests.\n\nPytest provides more assertions and a flexible library of assertions to fulfill various testing needs.\n\nThe following are some of the commonly used Pytest interface automation test assertions:\n\n- **Equality assertion**: checks whether two values are equal.\n\n   ```python\n   assert actual_value == expected_value\n   ```\n\n- **Unequality Assertion**: checks if two values are not equal.\n\n   ```python\n   assert actual_value != expected_value\n   ```\n\n- **Containment assertion**: checks whether a value is contained in another value, usually used to check whether a string contains a substring.\n\n   ```python\n   assert substring in full_string\n   ```\n\n- **Membership Assertion**: checks whether a value is in a collection, list, or other iterable object.\n\n   ```python\n   assert item in iterable\n   ```\n\n- **Truth Assertion**: checks whether an expression or variable is true.\n\n   ```python\n   assert expression\n   ```\n\n   OR\n\n   ```python\n   assert variable\n   ```\n\n- **False Value Assertion**: checks whether an expression or variable is false.\n\n   ```python\n   assert not expression\n   ```\n\n   OR\n\n   ```python\n   assert not variable\n   ```\n\n- **Greater Than, Less Than, Greater Than Equal To, Less Than Equal To Assertion**: checks whether a value is greater than, less than, greater than equal to, or less than equal to another value.\n\n   ```python\n   assert value \u003e other_value\n   assert value \u003c other_value\n   assert value \u003e= other_value\n   assert value \u003c= other_value\n   ```\n\n- **Type Assertion**: checks that the type of a value is as expected.\n\n   ```python\n   assert isinstance(value, expected_type)\n   ```\n\n   For example, to check if a value is a string:\n\n   ```python\n   assert isinstance(my_string, str)\n   ```\n\n- **Exception Assertion**: checks to see if a specific type of exception has been raised in a block of code.\n\n   ```python\n   with pytest.raises(ExpectedException):\n       # Block of code that is expected to raise an ExpectedException.\n   ```\n\n- **Approximate Equality Assertion**: checks whether two floating-point numbers are equal within some margin of error.\n\n   ```python\n   assert math.isclose(actual_value, expected_value, rel_tol=1e-9)\n   ```\n\n- **List Equality Assertion**: checks if two lists are equal.\n\n   ```python\n   assert actual_list == expected_list\n   ```\n\n- **Dictionary Equality Assertion**: checks if two dictionaries are equal.\n\n   ```python\n   assert actual_dict == expected_dict\n   ```\n\n- **Regular Expression Match Assertion**: checks if a string matches the given regular expression.\n\n   ```python\n   import re\n\n   assert re.match(pattern, string)\n   ```\n\n- **Null Assertion**: checks whether a value is `None`。\n\n   ```python\n   assert value is None\n   ```\n\n- **Non-null value assertion**: checks if a value is not `None`。\n\n   ```python\n   assert value is not None\n   ```\n\n- **Boolean Assertion**: checks whether a value of `True` or `False`。\n\n   ```python\n   assert boolean_expression\n   ```\n\n- **Empty Container Assertion**: checks if a list, collection or dictionary is empty.\n\n   ```python\n   assert not container  # Check if the container is empty\n   ```\n\n- **Contains Subset Assertion**: checks whether a set contains another set as a subset.\n\n   ```python\n   assert subset \u003c= full_set\n   ```\n\n- **String Beginning or End Assertion**: checks whether a string begins or ends with the specified prefix or suffix.\n\n    ```python\n    assert string.startswith(prefix)\n    assert string.endswith(suffix)\n    ```\n\n- **Quantity Assertion**: checks the number of elements in a list, collection, or other iterable object.\n\n    ```python\n    assert len(iterable) == expected_length\n    ```\n\n- **Range Assertion**: checks if a value is within the specified range.\n\n    ```python\n    assert lower_bound \u003c= value \u003c= upper_bound\n    ```\n\n- **Document Existence Assertion**: checking whether a document exists or not。\n\n    ```python\n    import os\n\n    assert os.path.exists(file_path)\n    ```\n\nThese are some common Pytest assertions, but depending on your specific testing needs, you may want to use other assertions or combine multiple assertions to more fully validate your test results.\nDetailed documentation on assertions can be found on the official Pytest website at:[Pytest - Built-in fixtures, marks, and nodes](https://docs.pytest.org/en/latest/reference.html#pytest)\n\n### Data-driven\n\nIn the process of API automation testing. The use of data-driven is a regular testing methodology where the input data and expected output data of the test cases are stored in data files, and the testing framework executes multiple tests based on these data files to validate various aspects of the API.\n\nThe test data can be easily modified without modifying the test case code.\n\nData-driven testing helps you cover multiple scenarios efficiently and ensures that the API works properly with a variety of input data.\n\nRefer to the demo:\u003chttps://github.com/Automation-Test-Starter/Pytest-API-Test-Demo\u003e\n\n#### Create the test configuration file\n\n\u003e Configuration file will be stored in json format for example, other formats such as YAML, CSV, etc. are similar, can be referred to.\n\n```bash\n// create a new config folder\nmkdir config\n// enter the config folder\ncd config\n// create a new configuration file\ntouch config.json\n```\n\n#### Writing Test Configuration Files\n\nThe configuration file stores the configuration information of the test environment, such as the URL of the test environment, database connection information, and so on.\n\nThe contents of the test configuration file in the demo are as follows:\n\n- Configure host information\n- Configure the getAPI interface information.\n- Configure the postAPI interface information.\n\n```json\n{\n  \"host\": \"https://jsonplaceholder.typicode.com\",\n  \"getAPI\": \"/posts/1\",\n  \"postAPI\":\"/posts\"\n}\n```\n\n#### Create the test data file\n\nThe request data file and the response data file store the request data and the expected response data of the test case, respectively.\n\n```bash\n// create a new data folder\nmkdir data\n// enter the data folder\ncd data\n// create a new request data file\ntouch request_data.json\n// create a new response data file\ntouch response_data.json\n```\n\n#### Writing test data files\n\n- Writing the request data file\n\n\u003e The request data file is configured with the request data for the getAPI API and the request data for the postAPI API.\n\n```json\n{\n  \"getAPI\": \"\",\n  \"postAPI\":{\n    \"title\": \"foo\",\n    \"body\": \"bar\",\n    \"userId\": 1\n  }\n}\n```\n\n- Writing the response data file\n\n\u003e The request data file is configured with the response data for the getAPI API and the response data for the postAPI API.\n\n```json\n{\n    \"getAPI\": {\n      \"userId\": 1,\n      \"id\": 1,\n      \"title\": \"sunt aut facere repellat provident occaecati excepturi optio reprehenderit\",\n      \"body\": \"quia et suscipit\\nsuscipit recusandae consequuntur expedita et cum\\nreprehenderit molestiae ut ut quas totam\\nnostrum rerum est autem sunt rem eveniet architecto\"\n    },\n    \"postAPI\":{\n      \"title\": \"foo\",\n      \"body\": \"bar\",\n      \"userId\": 1,\n      \"id\": 101\n    }\n}\n```\n\n#### Updating test cases to support data driving\n\n\u003e To differentiate, here is a new test case file named test_demo_data_driving.py\n\n```python\nimport requests\nimport json\n\n# get the test configuration information from the configuration file\nwith open(\"config/config.json\", \"r\") as json_file:\n    config = json.load(json_file)\n\n# get the request data from the test data file\nwith open('data/request_data.json', 'r') as json_file:\n    request_data = json.load(json_file)\n\n# get the response data from the test data file\nwith open('data/response_data.json', 'r') as json_file:\n    response_data = json.load(json_file)\n\n\nclass TestPytestDemo:\n\n    def test_get_demo(self):\n        host = config.get(\"host\")\n        get_api = config.get(\"getAPI\")\n        get_api_response_data = response_data.get(\"getAPI\")\n        # send request\n        response = requests.get(host+get_api)\n        # assert\n        assert response.status_code == 200\n        assert response.json() == get_api_response_data\n\n    def test_post_demo(self):\n        host = config.get(\"host\")\n        post_api = config.get(\"postAPI\")\n        post_api_request_data = request_data.get(\"postAPI\")\n        post_api_response_data = response_data.get(\"postAPI\")\n        # send request\n        response = requests.post(host + post_api, post_api_request_data)\n        # assert\n        assert response.status_code == 201\n        assert response.json() == post_api_response_data\n```\n\n#### Run the test case to confirm the data driver is working\n\n\u003e If you run the data driver support test case with demo project: test_demo_data_driving.py, it is recommended to block other test cases first, otherwise it may report errors.\n  \n```shell\n  pytest tests/test_demo_data_driving.py\n```\n\n![XQIPLf](https://cdn.jsdelivr.net/gh/naodeng/blogimg@master/uPic/XQIPLf.png)\n\n### Multi-environment support\n\nIn the actual API automation testing process, we need to run test cases in different environments to ensure that the API works properly in each environment.\n\nBy using Pytest's fixture feature, we can easily support multiple environments.\n\nRefer to the demo:\u003chttps://github.com/Automation-Test-Starter/Pytest-API-Test-Demo\u003e\n\n#### New test configuration files for different environments\n\n\u003e Configuration file will be stored in json format for example, other formats such as YAML, CSV, etc. are similar, can refer to the\n\n```bash\n// Create a new test configuration folder\nmkdir config\n// Go to the test configuration folder \ncd config\n// Create a new test configuration file for the development environment\ntouch dev_config.json\n// Create a new test configuration file for the production environment\ntouch prod_config.json\n```\n\n#### Writing different environment test profiles\n\n- Writing Development Environment Test Profiles\n\n\u003e Configure the development environment test profiles according to the actual situation.\n\n```json\n{\n  \"host\": \"https://jsonplaceholder.typicode.com\",\n  \"getAPI\": \"/posts/1\",\n  \"postAPI\":\"/posts\"\n}\n```\n\n- Configuring Production Environment Test Profiles\n\n\u003e Configure production environment test profiles according to the actual situation\n\n```json\n{\n  \"host\": \"https://jsonplaceholder.typicode.com\",\n  \"getAPI\": \"/posts/1\",\n  \"postAPI\":\"/posts\"\n}\n```\n\n#### New Different Environment Test Data File\n\n\u003e The different environments request data file and the response data file store the different environments request data and the different environments expected response data for the test cases, respectively.\n\n```bash\n// Create a new test data folder\nmkdir data\n// Go to the test data folder\ncd data\n// Create a new dev request data file\ntouch dev_request_data.json\n// Create a new dev response data file\ntouch dev_response_data.json \n// Create a new request data file for the production environment\ntouch prod_request_data.json \n// Create a new production response data file\ntouch prod_response_data.json \n```\n\n#### Writing test data files for different environments\n\n- Write the dev environment request data file\n\n\u003e The dev environment request data file is configured with the request data for the getAPI API and the request data for the postAPI API.\n\n```json\n{\n  \"getAPI\": \"\",\n  \"postAPI\":{\n    \"title\": \"foo\",\n    \"body\": \"bar\",\n    \"userId\": 1\n  }\n}\n```\n\n- Writing the dev Environment Response Data File\n\n\u003e The dev environment response data file is configured with the response data for the getAPI API and the response data for the postAPI API.\n\n```json\n{\n    \"getAPI\": {\n      \"userId\": 1,\n      \"id\": 1,\n      \"title\": \"sunt aut facere repellat provident occaecati excepturi optio reprehenderit\",\n      \"body\": \"quia et suscipit\\nsuscipit recusandae consequuntur expedita et cum\\nreprehenderit molestiae ut ut quas totam\\nnostrum rerum est autem sunt rem eveniet architecto\"\n    },\n    \"postAPI\":{\n      \"title\": \"foo\",\n      \"body\": \"bar\",\n      \"userId\": 1,\n      \"id\": 101\n    }\n}\n```\n\n- Write the prod environment request data file\n\n\u003e The prod environment request data file is configured with the request data for the getAPI API and the request data for the postAPI API.\n\n```json\n{\n  \"getAPI\": \"\",\n  \"postAPI\":{\n    \"title\": \"foo\",\n    \"body\": \"bar\",\n    \"userId\": 1\n  }\n}\n```\n\n- Writing the prod Environment Response Data File\n\n\u003e The prod environment response data file is configured with the response data for the getAPI API and the response data for the postAPI API.\n\n```json\n{\n    \"getAPI\": {\n      \"userId\": 1,\n      \"id\": 1,\n      \"title\": \"sunt aut facere repellat provident occaecati excepturi optio reprehenderit\",\n      \"body\": \"quia et suscipit\\nsuscipit recusandae consequuntur expedita et cum\\nreprehenderit molestiae ut ut quas totam\\nnostrum rerum est autem sunt rem eveniet architecto\"\n    },\n    \"postAPI\":{\n      \"title\": \"foo\",\n      \"body\": \"bar\",\n      \"userId\": 1,\n      \"id\": 101\n    }\n}\n```\n\n#### Configure fixture to support multiple environments\n\nThe \u003e fixture will be stored in the conftest.py file as an example, other formats such as YAML, CSV, etc. are similar.\n\n- Create a new conftest.py file in the project root directory.\n\n```bash\n mkdrir conftest.py\n```\n\n- Writing the conftest.py file\n\n```python\n\nimport pytest\nimport json\nimport json\nimport os\n\n\n@pytest.fixture(scope=\"session\")\ndef env_config(request):\n    # get config file from different env\n    env = os.getenv('ENV', 'dev')\n    with open(f'config/{env}_config.json', 'r') as config_file:\n        config = json.load(config_file)\n    return config\n\n\n@pytest.fixture(scope=\"session\")\ndef env_request_data(request):\n    # get request data file from different env\n    env = os.getenv('ENV', 'dev')\n    with open(f'data/{env}_request_data.json', 'r') as request_data_file:\n        request_data = json.load(request_data_file)\n    return request_data\n\n\n@pytest.fixture (scope=\"session\")\ndef env_response_data(request):\n    # get response data file from different env\n    env = os.getenv('ENV', 'dev')\n    with open(f'data/{env}_response_data.json', 'r') as response_data_file:\n        response_data = json.load(response_data_file)\n    return response_data\n```\n\n#### Update test case to support multi environment\n\n\u003e To make a distinction, here is a new test case file named test_demo_multi_environment.py\n\n```python\nimport requests\nimport json\n\n\nclass TestPytestMultiEnvDemo:\n\n    def test_get_demo_multi_env(self, env_config, env_request_data, env_response_data):\n        host = env_config[\"host\"]\n        get_api = env_config[\"getAPI\"]\n        get_api_response_data = env_response_data[\"getAPI\"]\n        # send request\n        response = requests.get(host+get_api)\n        # assert\n        assert response.status_code == 200\n        assert response.json() == get_api_response_data\n\n    def test_post_demo_multi_env(self, env_config, env_request_data, env_response_data):\n        host = env_config[\"host\"]\n        post_api = env_config[\"postAPI\"]\n        post_api_request_data = env_request_data[\"postAPI\"]\n        post_api_response_data = env_response_data[\"postAPI\"]\n        # send request\n        response = requests.post(host + post_api, post_api_request_data)\n        # assert\n        assert response.status_code == 201\n        assert response.json() == post_api_response_data\n```\n\n#### Run this test case to confirm that multi-environment support is in effect\n\n- Run the dev environment test case\n\n```shell\nENV=dev pytest test_case/test_demo_multi_environment.py\n```\n\n![Wb0owW](https://cdn.jsdelivr.net/gh/naodeng/blogimg@master/uPic/Wb0owW.png)\n\n- Run the prod environment test case\n\n```shell\nENV=prod pytest test_case/test_demo_multi_environment.py\n```\n\n![2kITJT](https://cdn.jsdelivr.net/gh/naodeng/blogimg@master/uPic/2kITJT.png)\n\n### Integration with allure reporting\n\nallure is a lightweight, flexible, and easily extensible test reporting tool that provides a rich set of report types and features to help you better visualize your test results.\n\nallure reports can be integrated with Pytest to generate detailed test reports.\n\nRefer to the demo:\u003chttps://github.com/Automation-Test-Starter/Pytest-API-Test-Demo\u003e\n\n#### Install allure-pytest library\n\n```shell\npip install allure-pytest\n```\n\n\u003e To avoid conflicts between the previously installed pytest-html-reporter and the allure-pytest package, it is recommended to uninstall the pytest-html-reporter package first.\n\n```shell\npip uninstall pytest-html-reporter\n```\n\n#### Configuration allure-pytest library\n\nUpdate the pytest.ini file to specify where allure reports are stored\n\n```ini\n[pytest]\n# allure\naddopts = --alluredir ./allure-results\n```\n\n#### Adjusting test cases to support allure reporting\n\n\u003e To differentiate, create a new test case file here, named test_demo_allure.py\n\n```python\nimport allure\nimport requests\n\n\n@allure.feature(\"Test example API\")\nclass TestPytestAllureDemo:\n\n    @allure.story(\"Test example get endpoint\")\n    @allure.title(\"Verify the get API\")\n    @allure.description(\"verify the get API response status code and data\")\n    @allure.severity(\"blocker\")\n    def test_get_example_endpoint_allure(self, env_config, env_request_data, env_response_data):\n        host = env_config[\"host\"]\n        get_api = env_config[\"getAPI\"]\n        get_api_request_data = env_request_data[\"getAPI\"]\n        get_api_response_data = env_response_data[\"getAPI\"]\n        # send get request\n        response = requests.get(host + get_api)\n        # assert\n        print(\"response status code is\" + str(response.status_code))\n        assert response.status_code == 200\n        print(\"response data is\" + str(response.json()))\n        assert response.json() == get_api_response_data\n\n    @allure.story(\"Test example POST API\")\n    @allure.title(\"Verify the POST API\")\n    @allure.description(\"verify the POST API response status code and data\")\n    @allure.severity(\"Critical\")\n    def test_post_example_endpoint_allure(self, env_config, env_request_data, env_response_data):\n        host = env_config[\"host\"]\n        post_api = env_config[\"postAPI\"]\n        post_api_request_data = env_request_data[\"postAPI\"]\n        post_api_response_data = env_response_data[\"postAPI\"]\n        # send request\n        response = requests.post(host + post_api, json=post_api_request_data)\n        # assert\n        print(\"response status code is\" + str(response.status_code))\n        assert response.status_code == 201\n        print(\"response data is\" + str(response.json()))\n        assert response.json() == post_api_response_data\n```\n\n#### Run test cases to generate allure reports\n\n```shell\nENV=dev pytest test_case/test_demo_allure.py\n```\n\n#### View allure report\n\nRun the following command to view the allure report in the browser\n\n```shell\nallure serve allure-results\n```\n\n![Pr1E3W](https://cdn.jsdelivr.net/gh/naodeng/blogimg@master/uPic/Pr1E3W.png)\n\n![OsUO2e](https://cdn.jsdelivr.net/gh/naodeng/blogimg@master/uPic/OsUO2e.png)\n\n#### Adapting CI/CD processes to support allure reporting\n\n\u003e Github action is an example, other CI tools are similar.\n\nUpdate the contents of the .github/workflows/pytest.yml file to upload allure reports to GitHub.\n\n```yaml\nname: Pytest API Testing\n\non:\n  push:\n    branches: [ \"main\" ]\n  pull_request:\n    branches: [ \"main\" ]\n\npermissions:\n  contents: read\n\njobs:\n  Pytes-API-Testing:\n\n    runs-on: ubuntu-latest\n\n    steps:\n    - uses: actions/checkout@v3\n    - name: Set up Python 3.10\n      uses: actions/setup-python@v3\n      with:\n        python-version: \"3.10\"\n    - name: Install dependencies\n      run: |\n        python -m pip install --upgrade pip\n        pip install -r requirements.txt\n        \n    - name: Test with pytest\n      run: |\n        ENV=dev pytest\n\n    - name: Archive Pytest allure test report\n      uses: actions/upload-artifact@v3\n      with:\n        name: Pytest-allure-report\n        path: allure-results\n          \n    - name: Upload Pytest allure report to GitHub\n      uses: actions/upload-artifact@v3\n      with:\n        name: Pytest-allure-report\n        path: allure-results\n```\n\n#### View github action allure report\n\nIn GitHub, navigate to your repository. Click the Actions tab at the top, and then click the Pytest API Testing workflow on the left. You should see the workflow running, wait for the execution to complete, and then you can view the results.\n\n![Lz2pPh](https://cdn.jsdelivr.net/gh/naodeng/blogimg@master/uPic/Lz2pPh.png)\n\n### concurrent testing and distributed testing\n\nIn the daily process of API automation testing, concurrent execution of test cases is required to improve testing efficiency.\n\nSometimes it is also necessary to introduce distributed testing in order to run test cases on multiple machines at the same time, which can also better improve testing efficiency.\n\n`pytest-xdist` is a plugin for Pytest that provides some corresponding functionality, mainly for supporting concurrent and distributed testing.\n\n#### `pytest-xdist` Feature Introduction\n\n1. **Concurrently run tests**:\n   - Use the `-n` option: `pytest -n NUM` allows running tests concurrently, where `NUM` is the number of concurrent workers. This can speed up test execution, especially on computers with multiple CPU cores.\n\n   ```bash\n   pytest -n 3  # Start 3 concurrent workers to execute the test\n   ```\n\n2. **Distributed testing**:\n   - Use `pytest --dist=loadscope`: allows tests to be executed on multiple nodes and test runs can be completed faster with distributed testing.\n\n   ```bash\n   pytest --dist=loadscope\n   ```\n\n   - Use `pytest --dist=each`: run a set of tests per node, for distributed testing.\n\n   ```bash\n   pytest --dist=each\n   ```\n\n3. **Parameterized tests and Concurrency**:\n   - Use of `pytest.mark.run`: In conjunction with the `pytest.mark.run` tag, tests with different tags can optionally be run on different processes or nodes.\n\n   ```python\n   @pytest.mark.run(processes=2)\n   def test_example():\n       pass\n   ```\n\n4. **Distributed environment setup**:\n   - Use `pytest_configure_node`: you can configure the tests before running them on the node.\n\n   ```python\n   def pytest_configure_node(node):\n       node.slaveinput['my_option'] = 'some value'\n   ```\n\n   - Use `pytest_configure_node`: you can configure the tests before running them on the node.\n\n   ```python\n   def pytest_configure_node(node):\n       node.slaveinput['my_option'] = 'some value'\n   ```\n\n5. **Distributed test environment destruction**:\n   - Use `pytest_configure_node`: you can clean up after running tests on a node.\n\n   ```python\n   def pytest_configure_node(node):\n       # Configure the node\n       yield\n\n       # Perform cleanup after running tests on nodes\n       print(\"Cleaning up after test run on node %s\" % node.gateway.id)\n   ```\n\nThese are some of the features provided by `pytest-xdist` that can help you perform concurrent and distributed tests more efficiently to speed up test execution and increase efficiency. Be sure to consult the `pytest-xdist` documentation for more detailed information and usage examples before using it.\n\n#### Installing `pytest-xdist` dependency\n\n```shell\npip install pytest-xdist\n```\n\n#### Example of running a test case concurrently\n\n##### Execute test cases concurrently with 3 workers\n\nRun the following commands to see how long the test cases take to execute\n\n- Concurrent Execution\n\n```shell\npytest -n 3\n```\n\n![LKHRct](https://cdn.jsdelivr.net/gh/naodeng/blogimg@master/uPic/LKHRct.png)\n\n- Default Parallel Execution\n\n```shell\npytest\n```\n\n![5y442s](https://cdn.jsdelivr.net/gh/naodeng/blogimg@master/uPic/5y442s.png)\n\n`Parallel execution took 9.81s` while `Concurrent execution took 1.63s`, you can see that concurrent execution of test cases can greatly improve the Parallel of testing.\n\n##### concurrently executes the test cases with 3 workers, and each worker prints the progress of the test cases\n\n```shell\npytest -n 3 -v\n```\n\n![5krJia](https://cdn.jsdelivr.net/gh/naodeng/blogimg@master/uPic/5krJia.png)\n\nThe progress of the test is printed in the test results, which provides a better understanding of the execution of the test cases.\n\n#### Distributed testing example\n\n##### Distributed test where each node runs a set of tests\n\n```shell\npytest --dist=each\n```\n\n![W1akqS](https://cdn.jsdelivr.net/gh/naodeng/blogimg@master/uPic/W1akqS.png)\n\nDistributed testing allows for faster test runs.\n\n##### Distributed testing, where each node runs a set of tests and each worker prints the progress of the test cases\n\n```shell\npytest --dist=each -v\n```\n\n![sMlawH](https://cdn.jsdelivr.net/gh/naodeng/blogimg@master/uPic/sMlawH.png)\n\nThe progress of the test will be printed in the test results, so you can better understand the execution of the test cases.\n\n##### Distributed testing, each node runs a set of tests, and each worker prints the progress of the test cases, as well as the output of the test logs\n\n```shell\npytest --dist=each -v --capture=no\n```\n\n![RkNSDb](https://cdn.jsdelivr.net/gh/naodeng/blogimg@master/uPic/RkNSDb.png)\n\nThe output of the test log is printed in the test results, which gives a better understanding of the execution of the test cases.\n\n### Filtering test case execution\n\nIn the daily API testing process, we need to selectively execute test cases according to the actual situation in order to improve the testing efficiency.\n\nGenerally, when we use allure test reports, we can use the Allure tag feature to filter the use cases corresponding to the tag to execute the test, but the Pytest framework does not directly support running tests based on Allure tags. However, the Pytest framework does not directly support running tests based on Allure tags, so you can use Pytest markers to accomplish this.\n\nPytest provides a `marks` tagging feature that can be used to tag different types of test cases and then filter them for execution.\n\nThe general process is that you can mark tests with custom markers (e.g. Regression/Smoke) and then use pytest's -m option to run only those tests.\n\n#### Defining Pytest Markers\n\nEdit the pytest.ini file and add the following: customize the type of markers\n\n- Regression: Marks the use case for regression testing.\n- Smoke: mark it as a use case for smoke testing\n\n```ini\nmarkers =\n    Regression: marks tests as Regression\n    Smoke: marks tests as Smoke\n```\n\n#### Marking Test Cases\n\nThe operation steps are:\n\n- Introduce pytest\n- Mark the test case with `@pytest.mark`.\n\n\u003e To differentiate, create a new test case file named test_demo_filter.py.\n\n```python\nimport pytest\nimport requests\nimport json\n\n\nclass TestPytestMultiEnvDemo:\n\n    @pytest.mark.Regression  # mark the test case as regression\n    def test_get_demo_filter(self, env_config, env_request_data, env_response_data):\n        host = env_config[\"host\"]\n        get_api = env_config[\"getAPI\"]\n        get_api_response_data = env_response_data[\"getAPI\"]\n        # send request\n        response = requests.get(host+get_api)\n        # assert\n        assert response.status_code == 200\n        assert response.json() == get_api_response_data\n\n    @pytest.mark.Smoke  # mark the test case as smoke\n    def test_post_demo_filter(self, env_config, env_request_data, env_response_data):\n        host = env_config[\"host\"]\n        post_api = env_config[\"postAPI\"]\n        post_api_request_data = env_request_data[\"postAPI\"]\n        print(\"make the request\")\n        post_api_response_data = env_response_data[\"postAPI\"]\n        # Your test code here\n        response = requests.post(host + post_api, json=post_api_request_data)\n        print(\"verify the response status code\")\n        assert response.status_code == 201\n        print(\"verify the response data\")\n        assert response.json() == post_api_response_data\n```\n\n#### Filtering Test Case Execution\n\n- Running Regression-tagged test cases\n\n```shell\npytest -m Regression\n```\n\nThis command tells pytest to run only the tests labeled Regression.\n\n![d8dMGa](https://cdn.jsdelivr.net/gh/naodeng/blogimg@master/uPic/d8dMGa.png)\n\n- Running Smoke-tagged test cases\n\n```shell\npytest -m Smoke\n```\n\nThis command tells pytest to run only the tests labeled Smoke.\n\n![2023112014VOVT3v](https://cdn.jsdelivr.net/gh/naodeng/blogimg@master/uPic/2023112014VOVT3v.png)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fautomation-test-starter%2Fpytest-api-test-starter","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fautomation-test-starter%2Fpytest-api-test-starter","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fautomation-test-starter%2Fpytest-api-test-starter/lists"}