اختبارات Pytest مع إنشاء التقارير في Allure باستخدام Docker و Gitlab Pages والسيلينيوم جزئيًا

هذا النص مخصص للمختبرين المبتدئين الذين يرغبون في فهم كيفية إعداد تقارير عن الجاذبية بتاريخ الاختبارات ، وكذلك شرح مكان تخزينها حتى يتمكن أي عضو من فريقك من النظر في التقرير.



مثال على تقرير تم الحصول عليه في الجاذبية



مستودع العمل بكود العمل النهائي والبنية التحتية



ارتباط بالتقارير بعد إجراء الاختبارات



gitlab python, allure, docker, , . , , , . allure, . , UI , .



. , . windows, .



, python, , . pytest, , , . Dog API. gitlab, docker.



, :







  • API
  • allure
  • Gitlab Runner
  • .gitlab-ci.yml
  • UI




allure_pages .



:





  • conftest.py
  • requirements.txt — python
  • test_dog_api.py tests


, . :



python -m venv venv



PyCharm





, PyCharm . , .



  1. Add Interpreter , PyCharm ,




:



.



  • pip install requests — , Dog Api
  • pip install pytest — ,
  • pip install pytest-xdist
  • pip install allure-pytest — allure




pip freeze > requirements.txt.



requirements.txt .



, requirements.txt

allure-pytest==2.8.18
pytest==6.0.1
pytest-xdist==2.1.0
requests==2.24.0




API



, GET POST . . , . .





@pytest.fixture
def dog_api():
    return ApiClient(base_address="https://dog.ceo/api/")


. base_address, https://dog.ceo/api/. , GET POST .



class ApiClient:
    def __init__(self, base_address):
        self.base_address = base_address


GET .



    def get(self, path="/", params=None, headers=None):
        url = f"{self.base_address}{path}"
        return requests.get(url=url, params=params, headers=headers)


, requests. ApiClient path, params, headers get , requests.get(url=url, params=params, headers=headers). . , , , , requests.get.



POST. , , .



    def post(self, path="/", params=None, data=None, json=None, headers=None):
        url = f"{self.base_address}{path}"
        return requests.post(url=url, params=params, data=data, json=json, headers=headers)


conftest.py:



import pytest
import requests

class ApiClient:
    def __init__(self, base_address):
        self.base_address = base_address

    def post(self, path="/", params=None, data=None, json=None, headers=None):
        url = f"{self.base_address}{path}"
        return requests.post(url=url, params=params, data=data, json=json, headers=headers)

    def get(self, path="/", params=None, headers=None):
        url = f"{self.base_address}{path}"
        return requests.get(url=url, params=params, headers=headers)

@pytest.fixture
def dog_api():
    return ApiClient(base_address="https://dog.ceo/api/")




Dog API . , . , . , . , . test_dog_api.py



import pytest

def test_get_random_dog(dog_api):
    response = dog_api.get("breeds/image/random")

    with allure.step(" ,   "):
        assert response.status_code == 200, f"  ,  {response.status_code}"

    with allure.step(" .    json  ."):
        response = response.json()
        assert response["status"] == "success"

    with allure.step(f"   {response}"):
        with allure.step(f"      "):
            with allure.step(f"  - "):
                pass

@pytest.mark.parametrize("breed", [
    "afghan",
    "basset",
    "blood",
    "english",
    "ibizan",
    "plott",
    "walker"
])
def test_get_random_breed_image(dog_api, breed):
    response = dog_api.get(f"breed/hound/{breed}/images/random")
    response = response.json()
    assert breed in response["message"], f"      ,  {response}"

@pytest.mark.parametrize("file", ['.md', '.MD', '.exe', '.txt'])
def test_get_breed_images(dog_api, file):
    response = dog_api.get("breed/hound/images")
    response = response.json()
    result = '\n'.join(response["message"])
    assert file not in result, f"      {file}"

@pytest.mark.parametrize("breed", [
    "african",
    "boxer",
    "entlebucher",
    "elkhound",
    "shiba",
    "whippet",
    "spaniel",
    "dvornyaga"
])
def test_get_random_breed_images(dog_api, breed):
    response = dog_api.get(f"breed/{breed}/images/")
    response = response.json()
    assert response["status"] == "success", f"      {breed}"

@pytest.mark.parametrize("number_of_images", [i for i in range(1, 10)])
def test_get_few_sub_breed_random_images(dog_api, number_of_images):
    response = dog_api.get(f"breed/hound/afghan/images/random/{number_of_images}")
    response = response.json()
    final_len = len(response["message"])
    assert final_len == number_of_images, f"   {number_of_images},  {final_len}"


allure



allure. , . , .



with allure.step('step 1'): — , .

@allure.feature('Dog Api') @allure.story('Send few requests') — ,



allure, . allure,

API allure .



class ApiClient:
    def __init__(self, base_address):
        self.base_address = base_address

    def post(self, path="/", params=None, data=None, json=None, headers=None):
        url = f"{self.base_address}{path}"
        with allure.step(f'POST request to: {url}'):
            return requests.post(url=url, params=params, data=data, json=json, headers=headers)

    def get(self, path="/", params=None, headers=None):
        url = f"{self.base_address}{path}"
        with allure.step(f'GET request to: {url}'):
            return requests.get(url=url, params=params, headers=headers)


. , . . .



@allure.feature('Random dog')
@allure.story('         ')
def test_get_random_dog(dog_api):
    response = dog_api.get("breeds/image/random")

    with allure.step(" ,   "):
        assert response.status_code == 200, f"  ,  {response.status_code}"

    with allure.step(" .    json  ."):
        response = response.json()
        assert response["status"] == "success"

    with allure.step(f"   {response}"):
        with allure.step(f"      "):
            with allure.step(f"  - "):
                pass


. test_dog_api.py



import pytest
import allure

@allure.feature('Random dog')
@allure.story('         ')
def test_get_random_dog(dog_api):
    response = dog_api.get("breeds/image/random")

    with allure.step(" ,   "):
        assert response.status_code == 200, f"  ,  {response.status_code}"

    with allure.step(" .    json  ."):
        response = response.json()
        assert response["status"] == "success"

    with allure.step(f"   {response}"):
        with allure.step(f"      "):
            with allure.step(f"  - "):
                pass

@allure.feature('Random dog')
@allure.story('     ')
@pytest.mark.parametrize("breed", [
    "afghan",
    "basset",
    "blood",
    "english",
    "ibizan",
    "plott",
    "walker"
])
def test_get_random_breed_image(dog_api, breed):
    response = dog_api.get(f"breed/hound/{breed}/images/random")

    with allure.step(" .    json  ."):
        response = response.json()

    assert breed in response["message"], f"      ,  {response}"

@allure.feature('List of dog images')
@allure.story('       ')
@pytest.mark.parametrize("file", ['.md', '.MD', '.exe', '.txt'])
def test_get_breed_images(dog_api, file):
    response = dog_api.get("breed/hound/images")

    with allure.step(" .    json  ."):
        response = response.json()

    with allure.step("        "):
        result = '\n'.join(response["message"])

    assert file not in result, f"      {file}"

@allure.feature('List of dog images')
@allure.story('   ')
@pytest.mark.parametrize("breed", [
    "african",
    "boxer",
    "entlebucher",
    "elkhound",
    "shiba",
    "whippet",
    "spaniel",
    "dvornyaga"
])
def test_get_random_breed_images(dog_api, breed):
    response = dog_api.get(f"breed/{breed}/images/")

    with allure.step(" .    json  ."):
        response = response.json()

    assert response["status"] == "success", f"      {breed}"

@allure.feature('List of dog images')
@allure.story('    ')
@pytest.mark.parametrize("number_of_images", [i for i in range(1, 10)])
def test_get_few_sub_breed_random_images(dog_api, number_of_images):
    response = dog_api.get(f"breed/hound/afghan/images/random/{number_of_images}")

    with allure.step(" .    json  ."):
        response = response.json()

    with allure.step("      "):
        final_len = len(response["message"])

    assert final_len == number_of_images, f"   {number_of_images},  {final_len}"




, :



  • .gitlab-ci.yml — , yaml gitlab runner
  • gitlab-runner — , Go. . . gitlab runner, docker . "". .


devops - , . . docker desktop windows. , .gitlab-ci .



docker desktop, .




, gitlab.com. gitlab.com, . , .



Settings -> General -> Visibility, project features, permissions, Pipelines .





CI / CD. Settings -> CI / CD -> Runners





1 Gitlab Runner. .



Gitlab Runner



, . . .



  1. - , : C:\GitLab-Runner.
  2. x86 amd64 . gitlab-runner.exe.
  3. . ( powershell )
  4. . , () .


. , powershell, , . , , .



C:\gitlab_runners , gitlab-runner.exe



, :



  1. :

    ./gitlab-runner.exe register
  2. url, 2. , :

    https://gitlab.somesubdomain.com/
  3. 3. , :

    tJTUaJ7JxfL4yafEyF3k
  4. . UI . :

    Runner on windows for autotests
  5. , , .gitlab-ci.yml, . , .

    docker, windows
  6. , . docker

    docker
  7. image, . .gitlab-ci.yml

    python:3.8-alpine






, .



:

.\gitlab-runner.exe status



:

.\gitlab-runner.exe run



, Settings -> CI / CD -> Runners, - :





, . . .





, .



.gitlab-ci.yml



.gitlab-ci.yml. .



stages. . 4. stage — job, .



stages:
  - testing #  
  - history_copy #       
  - reports #  
  - deploy #    gitlab pages


. Testing



docker_job: #  job
  stage: testing #  stage,   

  tags:
    - docker #     gitlab ,    .    ,  ,     6   .
  image: python:3.8-alpine #   ,      .

  before_script:
    - pip install -r requirements.txt #         

  script:
    - pytest -n=4 --alluredir=./allure-results tests/test_dog_api.py #   (-n=4   ),       --alluredir=

  allow_failure: true #        ,   .

  artifacts: # ,   ,    .
    when: always #  
    paths:
      - ./allure-results #    
    expire_in: 1 day # ,     .        .


. history_copy



history_job: #  job
  stage: history_copy #   stage,   

  tags:
    - docker #     

  image: storytel/alpine-bash-curl #       ,         .     , ?

  script:
    - 'curl --location --output artifacts.zip "https://( ,  gitlab.example.com)/api/v4/projects/(  )/jobs/artifacts/master/download?job=pages&job_token=$CI_JOB_TOKEN"'  #   api     job,    .        .          
    - apk add unzip # ,          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 #  


. reports



allure_job: #  job
  stage: reports #  stage,   

  tags:
    - docker #     

  image: frankescobar/allure-docker-service #      allure.      .

  script:
     - allure generate -c ./allure-results -o ./allure-report #    ./allure-results   ./allure-report

  artifacts:
    paths:
      - ./allure-results #            
      - ./allure-report
    expire_in: 1 day
  rules:
    - when: always


. deploy



pages: #   job  ,       pages

  stage: deploy #  stage,   

  script:
    - mkdir public #   public.      gitlab pages    public
    - mv ./allure-report/* public #    public  .

  artifacts:
    paths:
      - public
  rules:
    - when: always


.gitlab-ci.yml



stages:
  - testing #  
  - history_copy #       
  - reports #  
  - deploy #    gitlab pages

docker_job: #  job
  stage: testing #  stage,   
  tags:
    - docker #     gitlab ,    .    ,  ,     6   .
  image: python:3.8-alpine #   ,      .
  before_script:
    - pip install -r requirements.txt #         
  script:
    - pytest -n=4 --alluredir=./allure-results tests/test_dog_api.py #   (-n=4   ),       --alluredir=
  allow_failure: true #        ,   .
  artifacts: # ,   ,    .
    when: always #  
    paths:
      - ./allure-results #    
    expire_in: 1 day # ,     .        .

history_job: #  job
  stage: history_copy #   stage,   
  tags:
    - docker #     
  image: storytel/alpine-bash-curl #       ,         .     , ?
  script:
    - 'curl --location --output artifacts.zip "https://( ,  gitlab.example.com)/api/v4/projects/(  )/jobs/artifacts/master/download?job=pages&job_token=$CI_JOB_TOKEN"'  #   api     job,    .        .          
    - apk add unzip # ,          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: #  job
  stage: reports #  stage,   
  tags:
    - docker #     
  image: frankescobar/allure-docker-service #      allure.      .
  script:
     - allure generate -c ./allure-results -o ./allure-report #    ./allure-results   ./allure-report
  artifacts:
    paths:
      - ./allure-results #            
      - ./allure-report
    expire_in: 1 day
  rules:
    - when: always

pages: #   job  ,       pages
  stage: deploy #  stage,   
  script:
    - mkdir public #   public.      gitlab pages    public
    - mv ./allure-report/* public #    public  .
  artifacts:
    paths:
      - public
  rules:
    - when: always




, :



  1. conftest.py
  2. tests
  3. requirements.txt (, )
  4. .gitlab-ci.yml


.



, . , . CI / CD -> Pipelines



, Ci , .gitlab-ci.yml.







, Settings -> Pages. pages 30 . .





. .





, , . .





. , stage history_job . .





, . .





UI



[services](https://docs.gitlab.com/ee/ci/services/). , script. UI job :



  services:
    - selenium/standalone-chrome:latest


, url, - url, . :



  • :
  • / __
  • / - ( Gitlab Runner v1.1.0 )


executor ( chromedriver , ) ui :



browser = webdriver.Remote(command_executor="http://selenium__standalone-chrome:4444/wd/hub")


, .gitlab-ci.yml:

selenium/standalone-chrome:latest

:

selenium__standalone-chrome



لإجراء اختباراتي ، حصلت على هذه الوظيفة. إذا أضفت الوظائف الثلاث التالية من المثال مع اختبارات api إليه ، يمكنك إجراء جميع الاختبارات والحصول على تقرير.



chrome_job:
  stage: testing
  services:
    - selenium/standalone-chrome
  image: python:3.8
  tags:
    - docker
  before_script:
    - pip install -r requirements.txt
  script:
    - pytest --alluredir=./allure-results tests/
  allow_failure: true
  artifacts:
    when: always
    paths:
      - ./allure-results
    expire_in: 1 day


روابط مفيدة



بالإضافة إلى الروابط الموجودة في المقالة ، أود أن أشارك القليل من الأشياء الأخرى التي يمكن أن تساعد في العمل مع الجاذبية و gitlab ci.



مثال على هذا المشروع على

رابط gitlab للتقارير بعد إجراء الاختبارات

مقال عن الجاذبية على habr

مقدمة إلى gitlab ci



محدث: شكرا للمعلقين على الإشارة إلى عدم الدقة وعدم اكتمال هذا الدليل. لقد قمت بتصحيح وإضافة ارتباط إلى مستودع العمل




All Articles