Thuta Learning
CI/CD with GitHub Actions
AdvancedDevOps & Toolsbeginner

Flaky Test တွေနဲ့ ယုံကြည်မှု ပြဿနာ

ဒီခန်းပြီးရင် ဘာတတ်သွားမလဲ

  • Flaky Test တွေနဲ့ ယုံကြည်မှု ပြဿနာ concept ကို နားလည်ရှင်းပြနိုင်ရန်
  • Diagram ကို ဖတ်ပြီး pipeline ထဲမှာ job တွေ ဘယ်လိုစီးဆင်းပြီး ဘာက gate လုပ်သလဲ ခြေရာခံနိုင်ရန်
  • Workflow file ကို ဖတ်ပြီး ဘယ် job တွေ ဘယ်အစဉ်လိုက် run မလဲ ကြိုတင်ပြောနိုင်ရန်

နားလည်ထားရမယ့် အချက်

flaky test — code အတူတူပေါ်မှာ တစ်ခါ အောင်၊ တစ်ခါ ကျတဲ့ test — ဟာ test လုံးဝ မရှိတာထက် ပိုဆိုးပါတယ်။ မရှိတဲ့ test က သိထားတဲ့ ကွာဟချက် တစ်ခုပါ။ flaky test ကတော့ ဘယ်သူမှ မယုံတဲ့ failure တွေ ထုတ်ပေးနေတယ်၊ ပြီးတော့ red build တစ်ခုကို အသုံးဝင်စေတာက အဲဒီ ယုံကြည်မှု တစ်ခုတည်းပါ။ "ပြန် run လိုက်ပါ" ဆိုတာ အလိုအလျောက် တုံ့ပြန်မှု ဖြစ်သွားပြီဆိုရင် suite ဟာ signal မဟုတ်တော့ဘဲ ဖြတ်သန်းခ တံခါးတစ်ခု ဖြစ်သွားပြီ၊ ပြီးတော့ သူဖမ်းမိတဲ့ ပထမဆုံး တကယ့် regression ကိုလည်း ကျန်တာတွေလိုပဲ ပြန် run ပြီး merge လုပ်လိုက်မှာပါ။

အကြောင်းရင်းတွေက မထူးဆန်းဘဲ နေရာတိုင်းမှာ ထပ်ခါထပ်ခါ ပေါ်တယ်။ အချိန်ဆိုင်ရာ — laptop ပေါ်မှာ လုံလောက်ပေမယ့် အလုပ်များနေတဲ့ shared runner ပေါ်မှာ မလုံလောက်တဲ့ sleep အတိအကျ၊ async update နဲ့ အပြိုင် run နေတဲ့ assertion။ shared state — database row တစ်ခုတည်း၊ temp file တစ်ခုတည်း၊ environment variable တစ်ခုတည်းကို ရေးတဲ့ test တွေ။ order dependence — အရင် test တစ်ခုက data ချန်ထားလို့သာ အောင်နေတဲ့ test၊ suite ကို shard ခွဲလိုက်တဲ့နေ့မှာ ပေါ်လာတယ်။ ခဏခဏ နှေးတတ်၊ rate limit မိတတ်တဲ့ service တွေဆီ တကယ့် network call တွေ။ ပုံစံကို သတိထားပါ — flakiness အများစုဟာ အချိန် ဒါမှမဟုတ် isolation အပေါ် ဝှက်ထားတဲ့ ယူဆချက် တစ်ခုဖြစ်ပြီး CI ရဲ့ မတူတဲ့ machine, မတူတဲ့ concurrency, မတူတဲ့ အစီအစဉ်က အဲဒါကို ဖောက်ပြလိုက်တာပါ။

retry နဲ့ quarantine ဟာ ထိန်းချုပ်နည်းတွေပါ၊ ကုသနည်း မဟုတ်ဘူး။ အားလုံးကို အလိုအလျောက် retry လုပ်တာက failure rate ကို ဝှက်ပစ်တယ်။ quarantine tag ကတော့ အနည်းဆုံး ယုံရတဲ့ suite ကို ယုံရဆဲ ဖြစ်အောင် ထားပေးပြီး flaky test ကလည်း တစ်နေရာမှာ ဆက် run ပြီး ဆက် report နေတယ်။ နှစ်မျိုးလုံးမှာ သက်တမ်းကုန်ဆုံးရက် ထားရမယ်။ တကယ် အလုပ်ဖြစ်တဲ့ စည်းကမ်းက flakiness ကို ပိုင်ရှင်နဲ့ ရက်ချိန်းပါတဲ့ defect တစ်ခုအဖြစ် သတ်မှတ်ဖို့ပါ — failure တိုင်းကို test အမည်နဲ့တကွ တာရှည်ခံတဲ့ report ထဲ မှတ်ပါ၊ အကြိမ်ရေအလိုက် အဆင့်ခွဲပါ၊ အဆိုးဆုံးတွေကို ပြင် ဒါမှမဟုတ် ဖျက်ပါ၊ quarantine မှာ ဘယ်လောက်ကြာ နေခွင့်ရှိလဲ ကန့်သတ်ပါ။ ထွက်ပေါက် မရှိတဲ့ quarantine စာရင်းဟာ test တွေကို နှေးနှေးချင်း ဖျက်ပစ်နေတာပါပဲ။

text
THE TRUST EROSION LOOP
----------------------
  +--> a test fails for a reason unrelated to the change
  |             |
  |             v
  |     developer re-runs the job
  |             |
  |             v
  |     it passes; nobody investigates the first failure
  |             |
  |             v
  |     "red usually means flaky" becomes team folklore
  |             |
  |             v
  |     a REAL failure is re-run, goes green, and is merged
  +-------------+   (the suite now costs time and protects nothing)

BREAK THE LOOP
  quarantine  -> the trusted suite stays believable
  report      -> every failure is recorded with its test name
  expiry      -> quarantined tests are fixed or deleted, not kept

လက်တွေ့ scenario နဲ့ ချိတ်ကြည့်မယ်

ဒီ workflow က suite ကို နှစ်ပိုင်း ခွဲထားတယ်။ test job က quarantine tag မပါတဲ့ test တွေကိုပဲ run တယ် — ဒါက merge ကို ပိတ်ခွင့်ရှိတဲ့ ယုံကြည်ရတဲ့ suite ပါ။ ဒီ job ကျရင် တကယ့် ပြဿနာ တစ်ခုခု ရှိတယ်လို့ ယူဆလို့ ရမယ်၊ ဒါက ဒီ pattern တစ်ခုလုံးရဲ့ ရည်ရွယ်ချက်ပါ။

quarantined job က continue-on-error: true နဲ့ run တယ်၊ ဒါကြောင့် ကျပေမယ့် PR ကို မပိတ်ဘူး၊ ဒါပေမယ့် ဆက် run နေတယ်။ --repeat=5 က တစ်ခါတည်း run တာထက် flakiness ကို ပိုမြန်မြန် ဖော်ထုတ်ပေးတယ် — အောင်တစ်ခါ ကျတစ်ခါ ဆိုတာ တစ်ကြိမ်တည်း run လို့ မမြင်ရဘူး။ နှစ်ခုလုံးက JUnit report ကို artifact အဖြစ် တင်ထားပြီး retention-days: 30 က တစ်လစာ သမိုင်း ကျန်စေတယ်။ ဒီ report တွေ မရှိရင် "ဘယ် test က အကြိမ်ရေ အများဆုံး ကျလဲ" ဆိုတဲ့ မေးခွန်းကို ဖြေလို့ မရဘူး၊ ဖြေလို့မရရင် ဘယ်ဟာကို အရင်ပြင်ရမလဲ ဆုံးဖြတ်လို့ မရဘူး။

schedule trigger က nightly run ပေးထားတယ်။ ဒါက အရေးကြီးတယ် — code မပြောင်းဘဲ run တဲ့အခါ ကျတဲ့ test ဟာ ဘယ်လိုမှ ငြင်းလို့မရတဲ့ flaky သက်သေပါ။ PR run တွေမှာဆိုရင် "ဒီ PR ကြောင့်လား" ဆိုပြီး အငြင်းပွားစရာ ရှိသေးတယ်။ if: always() ကို upload step တွေမှာ ထားထားတာက test ကျတဲ့အခါမှာမှ report အလိုအပ်ဆုံး ဖြစ်လို့ပါ။

အတူတူ စမ်းရေးကြည့်မယ်

yaml
name: Test Health
on:
  pull_request:
    branches:
      - main
  schedule:
    - cron: '0 3 * * *'
permissions:
  contents: read
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: npm
      - run: npm ci
      - name: Run the trusted suite without quarantined tests
        run: npm test -- --exclude-tag=quarantined --reporter=junit
      - uses: actions/upload-artifact@v4
        if: always()
        with:
          name: junit-trusted
          path: reports/junit.xml
          retention-days: 30
  quarantined:
    runs-on: ubuntu-latest
    continue-on-error: true
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: npm
      - run: npm ci
      - name: Repeat quarantined tests to measure how often they fail
        run: npm test -- --only-tag=quarantined --repeat=5 --reporter=junit
      - uses: actions/upload-artifact@v4
        if: always()
        with:
          name: junit-quarantined
          path: reports/junit.xml
          retention-days: 30
      - name: Fail the run if anything has been quarantined too long
        run: ./scripts/check-quarantine-expiry.sh --max-age-days 30
You should see
pull request တိုင်းနဲ့ ညတိုင်း schedule မှာ job နှစ်ခုလုံး run တယ်။ test job က quarantine မလုပ်ထားတဲ့ test တွေကိုပဲ run လို့ သူကျရင် တကယ့် regression ဖြစ်နိုင်ခြေ မြင့်တယ်၊ ဒါကြောင့် merge ကို ပိတ်သင့်တဲ့ check က သူပါ။ quarantined job ကတော့ continue-on-error: true ဖြစ်လို့ အထဲက test တွေ ကျပေမယ့် pull request ကို မပိတ်ဘူး၊ ဒါပေမယ့် run လုပ်ပြီး JUnit report ကို artifact အဖြစ် တင်ပေးတယ် — အောင်ကျ အချိုးကို ရေရှည် ခြေရာခံနိုင်အောင်ပါ။ upload step တွေမှာ if: always() ရှိလို့ test ကျတဲ့ run တွေမှာလည်း report ရနေမယ်။ နောက်ဆုံး step က quarantine ထဲမှာ သတ်မှတ်ရက် ကျော်နေတဲ့ test ရှိရင် fail ပြန်ပေးပေမယ့် continue-on-error ကြောင့် အဲဒီ job တစ်ခုတည်းကိုပဲ ထိခိုက်စေတယ်။

၅ မိနစ် စမ်းကြည့်

ကိုယ့် repository ရဲ့ main branch ပေါ်မှာ code မပြောင်းဘဲ တစ်ညမှာ ငါးကြိမ် run တဲ့ scheduled workflow တစ်ခု ထည့်ပါ။ တစ်ပတ်လောက် စောင့်ပြီး JUnit artifact တွေကို စုပြီး ကျတဲ့ test အမည်အလိုက် အကြိမ်ရေ ရေတွက်ပါ။ အများဆုံး ကျတဲ့ သုံးခုကို ရွေးပြီး တစ်ခုချင်း ဘယ်အကြောင်းရင်း (timing, shared state, order, network) ကြောင့်လဲ ခွဲခြားပြီး၊ retry မထည့်ဘဲ တကယ် ပြင်ကြည့်ပါ။

သတိလေးတစ်ချက်

test runner ရဲ့ retry ကို suite တစ်ခုလုံးအတွက် ဖွင့်ထားလိုက်တာ — flaky test တွေ ဆက်တိုးနေပေမယ့် ဘယ်တော့မှ မမြင်ရတော့ဘူး၊ ပြီးတော့ တကယ့် race condition bug တွေကိုပါ ဖုံးဖိပေးလိုက်တယ်

quarantine လုပ်ပြီးတဲ့ test ကို CI ကနေ လုံးဝ ဖယ်ထုတ်လိုက်တာ — အကြိမ်ရေ data မရှိတော့လို့ ဘယ်တော့မှ ပြန်ပြင်ဖြစ်တော့ဘူး

GitHub Docs: Re-running workflows and jobsCI/CD with GitHub Actions

ဒီနေရာမှာ လူအများမှားတတ်တယ်

  • test runner ရဲ့ retry ကို suite တစ်ခုလုံးအတွက် ဖွင့်ထားလိုက်တာ — flaky test တွေ ဆက်တိုးနေပေမယ့် ဘယ်တော့မှ မမြင်ရတော့ဘူး၊ ပြီးတော့ တကယ့် race condition bug တွေကိုပါ ဖုံးဖိပေးလိုက်တယ်
  • quarantine လုပ်ပြီးတဲ့ test ကို CI ကနေ လုံးဝ ဖယ်ထုတ်လိုက်တာ — အကြိမ်ရေ data မရှိတော့လို့ ဘယ်တော့မှ ပြန်ပြင်ဖြစ်တော့ဘူး
  • Workflow ကို production branch ပေါ် တိုက်ရိုက်မစမ်းဘဲ branch (သို့) test repository တွင် အရင်အတည်ပြုပါ။

လေ့ကျင့်ခန်း

ကိုယ့် repository ရဲ့ main branch ပေါ်မှာ code မပြောင်းဘဲ တစ်ညမှာ ငါးကြိမ် run တဲ့ scheduled workflow တစ်ခု ထည့်ပါ။ တစ်ပတ်လောက် စောင့်ပြီး JUnit artifact တွေကို စုပြီး ကျတဲ့ test အမည်အလိုက် အကြိမ်ရေ ရေတွက်ပါ။ အများဆုံး ကျတဲ့ သုံးခုကို ရွေးပြီး တစ်ခုချင်း ဘယ်အကြောင်းရင်း (timing, shared state, order, network) ကြောင့်လဲ ခွဲခြားပြီး၊ retry မထည့်ဘဲ တကယ် ပြင်ကြည့်ပါ။

You'll know it worked when: pull request တိုင်းနဲ့ ညတိုင်း schedule မှာ job နှစ်ခုလုံး run တယ်။ test job က quarantine မလုပ်ထားတဲ့ test တွေကိုပဲ run လို့ သူကျရင် တကယ့် regression ဖြစ်နိုင်ခြေ မြင့်တယ်၊ ဒါကြောင့် merge ကို ပိတ်သင့်တဲ့ check က သူပါ။ quarantined job ကတော့ continue-on-error: true ဖြစ်လို့ အထဲက test တွေ ကျပေမယ့် pull request ကို မပိတ်ဘူး၊ ဒါပေမယ့် run လုပ်ပြီး JUnit report ကို artifact အဖြစ် တင်ပေးတယ် — အောင်ကျ အချိုးကို ရေရှည် ခြေရာခံနိုင်အောင်ပါ။ upload step တွေမှာ if: always() ရှိလို့ test ကျတဲ့ run တွေမှာလည်း report ရနေမယ်။ နောက်ဆုံး step က quarantine ထဲမှာ သတ်မှတ်ရက် ကျော်နေတဲ့ test ရှိရင် fail ပြန်ပေးပေမယ့် continue-on-error ကြောင့် အဲဒီ job တစ်ခုတည်းကိုပဲ ထိခိုက်စေတယ်။

Flaky Test တွေနဲ့ ယုံကြည်မှု ပြဿနာ | Thuta Learning