19-bo‘lim
Testlar CI quvurida
Testlarni har kommitda avtomatik ishga tushirish, belgilar bilan tez va sekin testlarni ajratish, parallel bajarish, hisobotlar va GitHub Actions ish oqimi.
Ushbu bo‘lim mundarijasi
Test faqat ishga tushirilganda foyda beradi. Mahalliy kompyuterda esa uni ishga tushirish oson unutiladi - ayniqsa shoshilinch tuzatish paytida.
CI (uzluksiz integratsiya) shu muammoni hal qiladi: testlar har kommitda avtomatik ishlaydi va hech kim ularni o'tkazib yubora olmaydi.
Testlarni guruhlash #
Katta loyihada barcha testni har kommitda ishga tushirish qimmat bo'lib qoladi. Yechim - ularni belgilar bilan ajratish:
[pytest]
addopts = -ra
markers =
sekin: sekin ishlaydigan testlar (tunda ishga tushiriladi)
integratsiya: baza yoki tarmoq talab qiladi
e2e: brauzer talab qiladi
import time
import pytest
def test_tez_birinchi():
assert 2 + 2 == 4
def test_tez_ikkinchi():
assert "a" + "b" == "ab"
@pytest.mark.sekin
def test_sekin_hisob():
time.sleep(0.4)
assert sum(range(100)) == 4950
@pytest.mark.integratsiya
def test_bazaga_yozish():
time.sleep(0.3)
assert True
Hamma testni ishga tushirish:
pytest -q -p no:randomly test_belgilar.py
.... [100%]
4 passed in 0.71s
Endi faqat tezlarini:
pytest -q -p no:randomly -m "not sekin and not integratsiya" test_belgilar.py
.. [100%]
2 passed, 2 deselected in 0.01s
Ikkita test tanlanmadi va vaqt sezilarli qisqardi.
| Qachon ishga tushiriladi | Buyruq |
|---|---|
| Har kommitda (tez qaytish aloqasi) | pytest -m "not sekin" |
| Birlashtirish so'rovida | pytest -m "not e2e" |
| Tunda yoki chiqarishdan oldin | pytest (hammasi) |
| Faqat bitta qism ustida ishlaganda | pytest -k "savat" |
-m "not sekin" yozganda, belgisi yo'q testlar ishlaydi - bu
to'g'ri. Lekin teskarisi xavfli: -m "sekin" faqat belgilanganlarni
oladi.
Agar quvuringiz -m "sekin" va -m "not sekin" ni ikki alohida
ishda bajarsa, hamma test qamraladi. Lekin uchinchi belgi qo'shsangiz
(e2e), uning testlari ikkala ishga ham tushmay qolishi mumkin.
Ishonch hosil qilishning oson yo'li - jami sonni solishtirish:
pytest --collect-only -q | tail -1 har ikki ishda yig'ilgan
sonlarning yig'indisiga teng bo'lsin.
Parallel ishga tushirish #
Testlar bir-biridan mustaqil bo'lsa (5-bo'limdagi qoida), ularni bir vaqtda ishga tushirish mumkin:
pip install pytest-xdist
Collecting pytest-xdist
Downloading pytest_xdist-3.8.0-py3-none-any.whl.metadata (3.0 kB)
Collecting execnet>=2.1 (from pytest-xdist)
Downloading execnet-2.1.2-py3-none-any.whl.metadata (2.9 kB)
Requirement already satisfied: pytest>=7.0.0 in ./.venv/lib/python3.13/site-packages (from pytest-xdist) (9.1.1)
Requirement already satisfied: iniconfig>=1.0.1 in ./.venv/lib/python3.13/site-packages (from pytest>=7.0.0->pytest-xdist) (2.3.0)
Requirement already satisfied: packaging>=22 in ./.venv/lib/python3.13/site-packages (from pytest>=7.0.0->pytest-xdist) (26.3)
Requirement already satisfied: pluggy<2,>=1.5 in ./.venv/lib/python3.13/site-packages (from pytest>=7.0.0->pytest-xdist) (1.6.0)
Requirement already satisfied: pygments>=2.7.2 in ./.venv/lib/python3.13/site-packages (from pytest>=7.0.0->pytest-xdist) (2.21.0)
Downloading pytest_xdist-3.8.0-py3-none-any.whl (46 kB)
Downloading execnet-2.1.2-py3-none-any.whl (40 kB)
Installing collected packages: execnet, pytest-xdist
Successfully installed execnet-2.1.2 pytest-xdist-3.8.0
Sakkizta sekin test yozamiz:
import time
import pytest
@pytest.mark.parametrize("raqam", range(8))
def test_sekin_amal(raqam):
time.sleep(0.3)
assert raqam * 2 == raqam + raqam
Ketma-ket:
pytest -q -p no:randomly test_parallel.py
........ [100%]
8 passed in 2.42s
To'rtta jarayonda:
pytest -q -p no:randomly -n 4 test_parallel.py
bringing up nodes...
bringing up nodes...
........ [100%]
8 passed in 1.00s
Vaqt taxminan to'rt barobar qisqardi. -n auto esa protsessor
yadrolari soniga qarab o'zi tanlaydi.
-n 4 qo'shganingizdan keyin testlar yiqila boshlasa, bu xushxabar:
siz allaqachon mavjud bo'lgan muammoni topdingiz.
Eng ko'p uchraydigan sabablar:
| Muammo | Yechim |
|---|---|
| Bitta fayl nomini ulashish | tmp_path ishlatish |
| Bitta ma'lumotlar bazasi | Har jarayonga alohida baza |
| Qat'iy port raqami | Portni tizimdan so'rash (13-bo'lim) |
| Umumiy kesh yoki global o'zgaruvchi | Holatni obyekt ichiga solish |
Bularning hammasi parallel bo'lmaganda ham xavfli - parallellik ularni faqat ko'rinadigan qiladi.
Hisobotlar #
CI tizimi natijani o'zi ko'rsatishi uchun mashinada o'qiladigan hisobot kerak:
pytest -q -p no:randomly --junitxml=natija.xml test_belgilar.py
.... [100%]
4 passed in 0.72s
Fayl XML - uni odam emas, mashina o'qiydi. Ichida nima borligini ko'ramiz:
import xml.etree.ElementTree as ET
toplam = ET.parse("natija.xml").getroot()[0]
print("jami:", toplam.get("tests"),
"| yiqilgan:", toplam.get("failures"),
"| xato:", toplam.get("errors"),
"| o'tkazilgan:", toplam.get("skipped"))
for holat in toplam:
print(f" {holat.get('classname')}.{holat.get('name')}")
python3 hisobot_oqi.py
jami: 4 | yiqilgan: 0 | xato: 0 | o'tkazilgan: 0
test_belgilar.test_tez_birinchi
test_belgilar.test_tez_ikkinchi
test_belgilar.test_sekin_hisob
test_belgilar.test_bazaga_yozish
junit-xml - deyarli barcha CI tizimlari tushunadigan format:
GitHub Actions, GitLab CI, Jenkins, TeamCity. Ular shu fayldan
yiqilgan testlarni chiqarib, birlashtirish so'rovida ko'rsatadi.
GitHub Actions ish oqimi #
Endi hammasini bitta faylga jamlaymiz:
name: Testlar
on:
push:
branches: [main]
pull_request:
jobs:
tez-testlar:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python: ["3.11", "3.12", "3.13"]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python }}
cache: pip
- name: Bog'liqliklarni o'rnatish
run: |
python -m pip install --upgrade pip
pip install -r talablar.txt
- name: Tez testlar
run: pytest -m "not sekin and not e2e" --junitxml=natija.xml
- name: Natijani saqlash
if: always()
uses: actions/upload-artifact@v4
with:
name: natija-${{ matrix.python }}
path: natija.xml
sekin-testlar:
runs-on: ubuntu-latest
needs: tez-testlar
if: github.event_name == 'push'
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.13"
cache: pip
- run: pip install -r talablar.txt
- run: pytest -m "sekin or e2e" -n auto
Faylning to'g'ri yozilganini tekshirib ko'ramiz:
pip install pyyaml
Collecting pyyaml
Downloading pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl.metadata (2.4 kB)
Downloading pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl (801 kB)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 801.6/801.6 kB 12.7 MB/s 0:00:00
Installing collected packages: pyyaml
Successfully installed pyyaml-6.0.3
python3 -c "import yaml, pathlib; d = yaml.safe_load(pathlib.Path('.github/workflows/testlar.yml').read_text()); print('ishlar:', list(d['jobs'])); print('matritsa:', d['jobs']['tez-testlar']['strategy']['matrix']['python'])"
ishlar: ['tez-testlar', 'sekin-testlar']
matritsa: ['3.11', '3.12', '3.13']
| Ish oqimidagi qaror | Nima uchun |
|---|---|
fail-fast: false | Bitta Python versiyasi yiqilsa ham, qolganlari ishlasin |
cache: pip | Bog'liqliklar har safar qayta yuklanmasin |
if: always() | Testlar yiqilsa ham hisobot saqlansin |
needs: tez-testlar | Sekin testlar faqat tezlari o'tgandan keyin |
if: github.event_name == 'push' | Har PR da emas, faqat birlashtirilgandan keyin |
| Matritsa | Kod bir necha Python versiyasida ishlashini tekshirish |
1. Yiqilgan quvur birlashtirishni to'sib qo'ysin. GitHub da bu branch protection sozlamasida yoqiladi. Aks holda yiqilgan testlar shunchaki e'tiborsiz qoladi.
2. Quvur tez bo'lsin. Agar tekshiruv 40 daqiqa olsa, dasturchilar natijani kutmasdan boshqa ishga o'tadi va qaytish aloqasi yo'qoladi. Maqsad - 10 daqiqadan kam.
3. Mahalliy va CI dagi buyruq bir xil bo'lsin. "Menda ishlaydi, CI da yo'q" holatlarining yarmi shundan kelib chiqadi: mahalliy kompyuterda boshqa bayroqlar bilan ishga tushirilgan.
CI/CD ni chuqurroq o'rganish uchun CI/CD va GitHub Actions darsligi ga qarang - u yerda quvur to'liq qurilgan.
Kommitdan oldin tekshirish #
CI - oxirgi to'siq, lekin birinchi to'siq mahalliy kompyuterda
bo'lishi kerak. Git ning pre-commit ilgagi shu ish uchun:
#!/bin/sh
# .git/hooks/pre-commit fayliga ko'chiring va bajariladigan qiling
echo "Tez testlar ishga tushirilmoqda..."
pytest -q -x -m "not sekin and not e2e" || {
echo "Testlar yiqildi - kommit to'xtatildi."
exit 1
}
sh ilgak.sh
Tez testlar ishga tushirilmoqda...
........... [100%]
11 passed, 1 deselected in 2.74s
-x bayrog'i birinchi yiqilishda to'xtatadi - ilgakda bu muhim,
chunki dasturchi javobni darhol kutib turadi.
Agar kommitdan oldingi tekshiruv 30 soniyadan uzoq davom etsa,
dasturchilar uni --no-verify bilan chetlab o'ta boshlaydi.
Ilgakka faqat eng tez tekshiruvlarni qo'ying: linter va modul testlari. Qolgani CI ning ishi.
pytest.inida uchta belgi e'lon qiling.- To'rtta test yozib, ikkitasini belgilang.
-m "not sekin"bilan ishga tushirib, vaqt farqini o'lchang.pytest --collect-only -qbilan jami testlar sonini ko'ring.pytest-xdisto'rnatib, sakkizta sekin testni ketma-ket va-n 4bilan solishtiring.- Testlardan biriga umumiy fayl qo'shing va parallel ishga tushiring - yiqiladimi?
--junitxmlbilan hisobot yaratib, uning birinchi qatorlarini o'qing.- Ish oqimi faylini yozib, uning to'g'ri YAML ekanini tekshiring.
- Matritsaga Python 3.10 ni qo'shing.
- O'z loyihangizda kommitdan oldingi ilgakni o'rnating va uning vaqtini o'lchang.
Xulosa #
- Test faqat ishga tushirilganda foyda beradi; CI uni har kommitda avtomatik bajaradi.
- Katta to'plamni belgilar bilan ajrating:
sekin,integratsiya,e2e. - Belgilar
pytest.inida e'lon qilinadi va-m "not sekin"bilan tanlanadi. - Quvurni bir necha ishga bo'lganda, jami test sonini solishtirib, hech biri tushib qolmaganiga ishonch hosil qiling.
- pytest-xdist testlarni bir necha jarayonda bajaradi:
-n 4yoki-n auto. - Parallel ishga tushirishdan keyin testlar yiqilsa - bu xushxabar: yashirin bog'liqlik topildi.
- Eng ko'p uchraydigan sabablar: umumiy fayl, umumiy baza, qat'iy port, global o'zgaruvchi.
--junitxml=natija.xmldeyarli barcha CI tizimlari tushunadigan hisobot yaratadi.- Ish oqimida
fail-fast: falsebitta versiya yiqilganda qolganlarini to'xtatmaydi. if: always()testlar yiqilganda ham hisobot saqlanishini ta'minlaydi.needs:bilan sekin testlar faqat tezlari o'tgandan keyin ishga tushadi.- Quvurdagi tartib tasodifiy emas: eng tez va eng arzon tekshiruv birinchi.
- Yiqilgan quvur birlashtirishni to'sib qo'yishi kerak, aks holda u e'tiborsiz qoladi.
- Quvur 10 daqiqadan kam vaqt olsin - aks holda qaytish aloqasi yo'qoladi.
- Mahalliy va CI dagi buyruq bir xil bo'lsin - "menda ishlaydi" holatlarining yarmi shundan.
- Kommitdan oldingi ilgak birinchi to'siq; u 30 soniyadan uzoq bo'lsa, chetlab o'tila boshlaydi.
Keyingi va oxirgi bo'limda hammasini birlashtiramiz: testi yo'q loyihaga to'liq test to'plami quramiz.
O‘qish tarixini saqlamoqchimisiz?
Tizimga kirsangiz, tugatgan bo‘limlaringiz saqlanadi va qoldirgan joyingizdan davom etasiz.
Xatolik topdingizmi?
Imlo xatosi, ishlamaydigan kod yoki noto‘g‘ri ma‘lumotni ko‘rsangiz - bizga xabar bering. Har bir xabar administrator tomonidan ko‘rib chiqiladi.