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.

🕑 17 daqiqa o‘qish 📄 939 so‘z 👁 0 marta ko‘rilgan
Ushbu bo‘lim mundarijasi
  1. Testlarni guruhlash
  2. Parallel ishga tushirish
  3. Hisobotlar
  4. GitHub Actions ish oqimi
  5. Kommitdan oldin tekshirish
  6. Xulosa

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:

INI
[pytest]
addopts = -ra
markers =
    sekin: sekin ishlaydigan testlar (tunda ishga tushiriladi)
    integratsiya: baza yoki tarmoq talab qiladi
    e2e: brauzer talab qiladi
Python
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:

Terminal
pytest -q -p no:randomly test_belgilar.py
Natija
....                                                             [100%]
4 passed in 0.71s

Endi faqat tezlarini:

Terminal
pytest -q -p no:randomly -m "not sekin and not integratsiya" test_belgilar.py
Natija
..                                                               [100%]
2 passed, 2 deselected in 0.01s

Ikkita test tanlanmadi va vaqt sezilarli qisqardi.

Qachon ishga tushiriladiBuyruq
Har kommitda (tez qaytish aloqasi)pytest -m "not sekin"
Birlashtirish so'rovidapytest -m "not e2e"
Tunda yoki chiqarishdan oldinpytest (hammasi)
Faqat bitta qism ustida ishlagandapytest -k "savat"
Belgisiz testlar jimgina tushib qolmasin

-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:

Terminal
pip install pytest-xdist
Natija
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:

Python
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:

Terminal
pytest -q -p no:randomly test_parallel.py
Natija
........                                                         [100%]
8 passed in 2.42s

To'rtta jarayonda:

Terminal
pytest -q -p no:randomly -n 4 test_parallel.py
Natija
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.

Parallel ishga tushirish yashirin bog'liqlikni ochib beradi

-n 4 qo'shganingizdan keyin testlar yiqila boshlasa, bu xushxabar: siz allaqachon mavjud bo'lgan muammoni topdingiz.

Eng ko'p uchraydigan sabablar:

MuammoYechim
Bitta fayl nomini ulashishtmp_path ishlatish
Bitta ma'lumotlar bazasiHar jarayonga alohida baza
Qat'iy port raqamiPortni tizimdan so'rash (13-bo'lim)
Umumiy kesh yoki global o'zgaruvchiHolatni 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:

Terminal
pytest -q -p no:randomly --junitxml=natija.xml test_belgilar.py
Natija
....                                                             [100%]
4 passed in 0.72s

Fayl XML - uni odam emas, mashina o'qiydi. Ichida nima borligini ko'ramiz:

Python
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')}")
Terminal
python3 hisobot_oqi.py
Natija
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:

YAML
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:

Terminal
pip install pyyaml
Natija
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
Terminal
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'])"
Natija
ishlar: ['tez-testlar', 'sekin-testlar']
matritsa: ['3.11', '3.12', '3.13']
Testlar quvurda qayerda turadi Kommit push yoki PR Linter ~10 soniya Tez testlar ~1-2 daqiqa Integratsiya ~5 daqiqa E2E va sekin tunda yoki relizda Tartib tasodifiy emas: eng tez va eng arzon tekshiruv birinchi ishlaydi
Yiqilish qanchalik erta bo'lsa, shunchalik arzon
Ish oqimidagi qarorNima uchun
fail-fast: falseBitta Python versiyasi yiqilsa ham, qolganlari ishlasin
cache: pipBog'liqliklar har safar qayta yuklanmasin
if: always()Testlar yiqilsa ham hisobot saqlansin
needs: tez-testlarSekin testlar faqat tezlari o'tgandan keyin
if: github.event_name == 'push'Har PR da emas, faqat birlashtirilgandan keyin
MatritsaKod bir necha Python versiyasida ishlashini tekshirish
Quvurning uchta muhim sozlamasi

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:

Terminal
#!/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
}
Terminal
sh ilgak.sh
Natija
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.

Ilgak sekin bo'lmasin

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.

Amaliy topshiriq
  1. pytest.ini da uchta belgi e'lon qiling.
  2. To'rtta test yozib, ikkitasini belgilang.
  3. -m "not sekin" bilan ishga tushirib, vaqt farqini o'lchang.
  4. pytest --collect-only -q bilan jami testlar sonini ko'ring.
  5. pytest-xdist o'rnatib, sakkizta sekin testni ketma-ket va -n 4 bilan solishtiring.
  6. Testlardan biriga umumiy fayl qo'shing va parallel ishga tushiring - yiqiladimi?
  7. --junitxml bilan hisobot yaratib, uning birinchi qatorlarini o'qing.
  8. Ish oqimi faylini yozib, uning to'g'ri YAML ekanini tekshiring.
  9. Matritsaga Python 3.10 ni qo'shing.
  10. 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.ini da 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 4 yoki -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.xml deyarli barcha CI tizimlari tushunadigan hisobot yaratadi.
  • Ish oqimida fail-fast: false bitta 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.

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.