هذا النص مخصص للمختبرين المبتدئين الذين يرغبون في فهم كيفية إعداد تقارير عن الجاذبية بتاريخ الاختبارات ، وكذلك شرح مكان تخزينها حتى يتمكن أي عضو من فريقك من النظر في التقرير.
مستودع العمل بكود العمل النهائي والبنية التحتية
ارتباط بالتقارير بعد إجراء الاختبارات
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
— pythontest_dog_api.py
tests
, . :
python -m venv venv
PyCharm
, PyCharm . , .
-
Add Interpreter
, PyCharm ,
:
.
pip install requests
— , Dog Apipip 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 runnergitlab-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
Gitlab Runner
. , powershell, , . , , .
C:\gitlab_runners , gitlab-runner.exe
, :
- :
./gitlab-runner.exe register
- url, 2. , :
https://gitlab.somesubdomain.com/
- 3. , :
tJTUaJ7JxfL4yafEyF3k
- . UI . :
Runner on windows for autotests
- , , .gitlab-ci.yml, . , .
docker, windows
- , . docker
docker
- image, .
.gitlab-ci.yml
python:3.8-alpine
, .
:
.\gitlab-runner.exe status
:
.\gitlab-runner.exe run
, Settings -> CI / CD -> Runners
, - :
, . . .
, .
.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
, :
conftest.py
-
tests
requirements.txt
(, ).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
محدث: شكرا للمعلقين على الإشارة إلى عدم الدقة وعدم اكتمال هذا الدليل. لقد قمت بتصحيح وإضافة ارتباط إلى مستودع العمل