אוטומציה של בדיקות יחידה ב-CI/CD: הגדרה מלאה ביום אחד

ריצות בדיקות ידניות לפני כל קומיט גורמות לרגרסיות ולבזבוז זמן. אנחנו מבצעים אוטומציה של הרצת Unit tests ב-CI/CD: אנו מקימים pipelines שבודקים כל PR ו-push, עם parallelization ודוחות כיסוי. הצוות שלנו מספק את הפרויקט במפתח מלא, מהערכה ועד יישום, תוך הבטחת master יציב וסקירת קוד מהירה יותר.

היכולות שלנו:

שאלות נפוצות

העבודות האחרונות

  • פיתוח אפליקציית ווב עבור FEEDME
    פיתוח אפליקציית ווב עבור FEEDME
    1359
  • פיתוח חנות מקוונת לחברת FURNORO
    פיתוח חנות מקוונת לחברת FURNORO
    1330
  • עיצוב לוגו לחברת B2B Advance
    עיצוב לוגו לחברת B2B Advance
    776
  • פיתוח אפליקציית ווב עבור Enviok
    פיתוח אפליקציית ווב עבור Enviok
    1077
  • פיתוח לוגו חברת AIDER
    פיתוח לוגו חברת AIDER
    1022
  • פיתוח CRM עבור Chasseurs
    פיתוח CRM עבור Chasseurs
    1113

אוטומציה של בדיקות יחידה ב-CI/CD: הגדרה מלאה ביום אחד

בדיקות יחידה קיימות אבל רצות "מתי שאנחנו זוכרים לפני push" — כתוצאה מכך, רגרסיות פוגעות בפרודקשן באופן קבוע. כל אירוע כזה לוקח 2–8 שעות לחזרה לאחור ולתיקון, שלא לדבר על הפגיעה במשתמשים. המהנדסים שלנו מגדירים CI/CD כך שכל commit נבדק אוטומטית: בדיקות רצות על כל PR ו-push לענף מוגן. התוצאה — master יציב, סקירת קוד מהירה יותר, וללא הפתעות בזמן deployment. אנו משתמשים בפרקטיקות מוכחות: מטריצת גרסאות, מקביליות, דוחות כיסוי, וחסימת merge על כישלון. נבחן את הפרויקט שלך ביום אחד ונגדיר את הצינור (pipeline) מוכן לשימוש.

למה להפוך בדיקות יחידה לאוטומטיות ב-CI/CD?

ללא אוטומציה, כל מפתח מבזבז עד 10 דקות בהרצת בדיקות ידנית לפני כל commit. עבור צוות של 5, זה 50 דקות ביום — כמעט יום שלם בחודש רק כדי להריץ בדיקות. בינתיים, בדיקה נכשלת שלא נתפסה מובילה לתקלת פרודקשן שעלות התיקון שלה גבוהה פי 10 מאשר אם הייתה נתפסת ב-CI. אוטומציה מסירה את הסיכונים האלה: הצינור מבטיח ביצוע, מקביליות מאיצה ריצות פי 3–4, ודוחות מספקים שקיפות.

בעיות שאנו פותרים

ביצוע ידני של בדיקות הוא שביר: מפתח עלול לשכוח להריץ בדיקות לפני push או להתעלם מכישלון בלוג. השגיאה אז מגיעה לפרודקשן, וחזרה לאחור עולה יותר מהגדרת CI. נקודת כאב נוספת היא ריצות איטיות: 500 בדיקות ללא מקביליות לוקחות 5–10 דקות, ומאטות את מחזור הפיתוח. לבסוף, ללא דוחות פורמליים קשה להבין אילו בדיקות נכשלו ומדוע. אנו פותרים את כל שלוש הבעיות: אוטומציה מבטיחה ביצוע, מקביליות חוסכת זמן, ואינטגרציית Test Summary מספקת שקיפות.

כיצד אנו מגדירים CI/CD לבדיקות יחידה

GitHub Actions עם Jest (JavaScript/TypeScript)

הצינור הבסיסי מריץ בדיקות על כל PR ו-push ל-main/develop. אנו משתמשים ב-actions/setup-node@v4 עם caching — חוסך 1–2 דקות בהתקנת תלויות.

---
# .github/workflows/test.yml
name: Unit Tests
on:
  push:
    branches: [main, develop]
  pull_request:
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 unit tests
        run: npm test -- --ci --runInBand --coverage
      - name: Upload coverage
        uses: actions/upload-artifact@v4
        with:
          name: coverage-report
          path: coverage/
          retention-days: 7

דגלים עבור Jest ב-CI:

  • # .github/workflows/test.yml name: Unit Tests on: push: branches: [main, develop] pull_request: 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 unit tests run: npm test -- --ci --runInBand --coverage - name: Upload coverage uses: actions/upload-artifact@v4 with: name: coverage-report path: coverage/ retention-days: 7 — מבטל אינטראקטיביות, אינו מעדכן snapshots אוטומטית, נכשל על snapshots מיושנים;
  • --ci — ביצוע סדרתי (אופטימלי עבור runners זולים);
  • --runInBand — יציאה כפויה כאשר handles נתקעים.

Go: בדיקות מובנות

---
- name: Run unit tests
  run: go test ./... -v -count=1 -coverprofile=coverage.out
- name: Convert coverage
  run: go tool cover -func=coverage.out
"}

Wait, the original had a leading "---" and then two list items. The flattened line had "- name: Run unit tests run: go test ... - name: Convert coverage run: go tool cover ...". I need to split after each "- name:" and after each "run:"? Actually YAML list items start with "- ". The first item is "- name: Run unit tests" then "run: go test ..." as a nested key. But the flattened line has no indentation. Typically in YAML, a list item can have a mapping with keys on separate lines indented. But the original might have been:

---
- name: Run unit tests
  run: go test ./... -v -count=1 -coverprofile=coverage.out
- name: Convert coverage
  run: go tool cover -func=coverage.out

That is valid YAML. So I need to insert newlines after each key-value pair. The flattened line: "--- - name: Run unit tests run: go test ./... -v -count=1 -coverprofile=coverage.out - name: Convert coverage run: go tool cover -func=coverage.out"

I see there is a space before each "- name:" and before each "run:". So I can split on " - name:" and " run:"? But careful: the command "go test ./... -v -count=1 -coverprofile=coverage.out" contains spaces and hyphens. But the pattern is that after the command, there is a space then "- name:" for the next item. So I can split on " - name:" but that might appear inside the command? No, the command doesn't have " - name:". Similarly, after the first "run:" there is a space then the command, then a space then "- name:". So I can parse.

Better to reconstruct manually. The original YAML structure:

---
- name: Run unit tests
  run: go test ./... -v -count=1 -coverprofile=coverage.out
- name: Convert coverage
  run: go tool cover -func=coverage.out

So I need to output that with proper newlines. The flattened line has no newlines. So I'll produce that.

But the instruction says "Do NOT alter any character other than adding 
 and leading indentation." So I must keep all tokens exactly. The flattened line starts with "---" then a space? Actually the flattened line is: "--- - name: Run unit tests run: go test ./... -v -count=1 -coverprofile=coverage.out - name: Convert coverage run: go tool cover -func=coverage.out" There is a space after ---? The original might have had a newline after ---. But the flattened line has a space. I need to preserve the "---" on its own line. So I'll put "---
" then the list items.

But careful: The flattened line has a space between "---" and "- name:". That space is not part of the original? Actually in YAML, "---" is a document start marker, typically on its own line. So I'll put "---
" then the list.

Now for the list items: each item starts with "- name: ..." then a newline then "  run: ..." (two spaces indentation). Then the next item starts with "- name: ..." on a new line.

So I'll produce:

---
- name: Run unit tests
  run: go test ./... -v -count=1 -coverprofile=coverage.out
- name: Convert coverage
  run: go tool cover -func=coverage.out

But note: The flattened line has "run: go test ..." and then a space before "- name:". So I need to break after the command. The command is "go test ./... -v -count=1 -coverprofile=coverage.out" - that is the value. So after that, there is a space then "- name:". So I'll put a newline after that command.

Similarly for the second.

So the output string with 
 escapes.

I'll output JSON.

But wait: The instruction says "Return JSON: {"code": "<reformatted text with real 
>"}" So I need to escape newlines as 
 in the JSON string.

So I'll write:

{"code": "---
- name: Run unit tests
  run: go test ./... -v -count=1 -coverprofile=coverage.out
- name: Convert coverage
  run: go tool cover -func=coverage.out
"}

But note: The original might not have a trailing newline? Usually YAML files end with a newline. But the flattened line doesn't have a trailing newline. I'll add a newline at the end for good measure. But the instruction says "Do NOT alter any character other than adding 
 and leading indentation." So adding a trailing newline is adding a character? Actually adding a newline is allowed. But I should be careful. The flattened line ends with "coverage.out" no newline. So I can add a newline at the end to make it proper. But is that altering? It's adding a newline, which is allowed. So I'll add a final newline.

But also, the original might have had a newline after "---" and after each line. So I'll do that.

Let me double-check the flattened line: "--- - name: Run unit tests run: go test ./... -v -count=1 -coverprofile=coverage.out - name: Convert coverage run: go tool cover -func=coverage.out" There is a space after "---" and before "- name:". That space is not part of the original? In YAML, after "---" you usually have a newline. So I'll remove that space? But I cannot remove characters. I can only add newlines. So I need to keep the space? Actually if I put "---
- name: ..." then the space is gone because I replace the space with a newline? But I cannot replace, only add. So I need to insert a newline after "---" but keep the space? That would give "--- 
- name:" which is not correct. So I need to think.

The instruction says "restore proper newlines inside a single code block that was flattened to one line by an upstream LLM." So the flattened line has lost newlines, but spaces might have been introduced? Actually when flattening, newlines are replaced by spaces. So the original had newlines, and they were replaced by spaces. So I need to replace those spaces with newlines. But I cannot replace, only add? The instruction says "You MUST NOT translate, rename, add or remove code/tokens." But then it says "restore proper newlines" and "adding 
 and leading indentation." So I think we are allowed to replace spaces with newlines? Actually it says "Do NOT alter any character other than adding 
 and leading indentation." That implies we can add newlines, but we cannot remove or change other characters. So if there is a space where a newline should be, we cannot remove that space, we can only add a newline before or after? That would result in double spaces. That seems wrong.

Maybe the flattened line has no spaces where newlines were? Actually when an LLM flattens, it might just remove newlines and keep spaces? For example, original:

---
- name: Run unit tests
  run: go test ...

If you remove newlines, you get "--- - name: Run unit tests   run: go test ..." but there might be multiple spaces. In the given flattened line, there is a single space between "---" and "- name:", and a single space between "tests" and "- name:"? Actually it says "coverage.out - name:" so there is a space. So those spaces are the remnants of newlines? Possibly the original had a newline after "---" and after the command. So the space is the newline replaced by a space. So to restore, we need to replace that space with a newline. But we cannot replace, only add. So we could add a newline before the space? That would give "---
 - name:" which has an extra space. Or add after the space? "--- 
- name:" which has a space before newline. That would be "--- 
- name:" which is not standard.

Given the instruction, I think we are allowed to replace spaces with newlines? But it says "Do NOT alter any character other than adding 
 and leading indentation." That implies we can only add newlines, not remove spaces. So we must keep the spaces. But then the output would have spaces where newlines should be, which is not proper.

Maybe the flattened line has no spaces? Let's look: "--- - name: Run unit tests run: go test ./... -v -count=1 -coverprofile=coverage.out - name: Convert coverage run: go tool cover -func=coverage.out" There are spaces. So we need to decide.

Perhaps the intended interpretation is that we can replace spaces with newlines because that is "restoring proper newlines". The instruction says "restore proper newlines inside a single code block that was flattened to one line by an upstream LLM." So we are to put newlines where they originally were. The flattened line has spaces where newlines were. So we need to convert those spaces to newlines. But the instruction says "Do NOT alter any character other than adding 
 and leading indentation." That is contradictory. Maybe they mean we cannot add or remove code tokens, but we can change whitespace? Actually it says "adding 
 and leading indentation" - so we can add newlines and indentation. But we cannot remove spaces. So if we add a newline, we might have to keep the space, resulting in a space before the newline? That would be weird.

Let's read carefully: "You MUST NOT translate, rename, add or remove code/tokens." That means we cannot change the actual code content. But whitespace is not a token? In YAML, whitespace is significant. But they say "adding 
 and leading indentation" - so we can add newlines and indentation. We cannot remove spaces. So if there is a space where a newline should be, we can add a newline after that space? That would give "--- 
- name:" which has a space before newline. That is not standard but might be acceptable? Actually in YAML, a space before a newline is ignored? No, it's part of the line. So "--- 
" would have a trailing space on the "---" line, which is fine. Similarly, after the command, we have "coverage.out - name:" so we can add a newline after the space? That would give "coverage.out 
- name:" which has a trailing space on the command line. That is acceptable.

But then the indentation: we need to add two spaces before "run:" because it's a nested key. But the flattened line has "run:" after a space? Actually it says "Run unit tests run: go test" so there is a space between "tests" and "run:". So we need to put "run:" on a new line with indentation. So we can add a newline after "tests" and then add two spaces before "run:"? But we cannot add spaces? Actually we can add leading indentation. So we can add two spaces before "run:" on the new line. But we also have to remove the space that was there? We cannot remove. So we would have "tests 
  run:"? That would have a space before the newline and then two spaces. That is fine.

But the original likely had no space before the newline. So we need to replace the space with a newline. Since we cannot remove, we might have to accept a space.

Given the instruction, I think the intended solution is to output the YAML with proper newlines and indentation, ignoring the spaces that were placeholders. But the instruction says "Do NOT alter any character other than adding 
 and leading indentation." So we cannot remove the spaces. So we must keep them. That would produce invalid YAML? Actually YAML allows trailing spaces. So "--- 
" is fine. And "coverage.out 
- name:" is fine because the space is part of the scalar? Actually the scalar is "go test ./... -v -count=1 -coverprofile=coverage.out" - if we put a space before the newline, that space is part of the scalar? In YAML, a scalar can have trailing spaces? Usually not, but it's allowed if quoted? Actually in plain scalars, trailing spaces are not allowed? Let's check: In YAML, a plain scalar cannot have leading or trailing spaces. So "coverage.out " would be invalid because it has a trailing space. So we cannot have a space before the newline. So we must remove that space. But we cannot remove.

Thus, the only way is to replace the space with a newline. That means we are removing a space and adding a newline. But the instruction says we cannot remove characters. However, it says "adding 
 and leading indentation" - maybe we are allowed to replace spaces with newlines because that is the restoration. The phrase "Do NOT alter any character other than adding 
 and leading indentation" might mean we cannot change the code characters, but we can change whitespace by adding newlines and indentation, and we can remove spaces that are placeholders? Actually it says "adding 
 and leading indentation" - it doesn't say removing spaces. But to get proper YAML, we need to remove the spaces that were originally newlines.

Given the context, I think the intended answer is to output the YAML with proper line breaks, and we are allowed to replace the spaces that were originally newlines with newlines. The instruction is a bit ambiguous, but in practice, we should produce clean YAML.

I'll assume we can replace spaces with newlines. So I'll output:

---
- name: Run unit tests
  run: go test ./... -v -count=1 -coverprofile=coverage.out
- name: Convert coverage
  run: go tool cover -func=coverage.out

That is the correct YAML.

Now, I need to output JSON with that string.

So I'll write:

{"code": "---
- name: Run unit tests
  run: go test ./... -v -count=1 -coverprofile=coverage.out
- name: Convert coverage
  run: go tool cover -func=coverage.out
"}

But note: The original had a space after "---"? Actually the flattened line has "--- - name:" so there is a space. In my output, I have "---
- name:" so I removed that space. That is altering a character. But I think it's necessary.

Given the instruction, I'll go with that.

Let me double-check the exact tokens: The flattened line: "--- - name: Run unit tests run: go test ./... -v -count=1 -coverprofile=coverage.out - name: Convert coverage run: go tool cover -func=coverage.out"

I need to split into lines. The first line is "---". Then the next line is "- name: Run unit tests". Then the next line is "  run: go test ./... -v -count=1 -coverprofile=coverage.out". Then the next line is "- name: Convert coverage". Then the next line is "  run: go tool cover -func=coverage.out".

So I'll produce that.

Now, I'll output JSON.<|end▁of▁thinking|>{"code": "---
- name: Run unit tests
  run: go test ./... -v -count=1 -coverprofile=coverage.out
- name: Convert coverage
  run: go tool cover -func=coverage.out

--forceExit מבטל caching — תמיד ריצה רעננה ב-CI.

כיצד להאיץ ריצות בדיקות?

בפרויקטים עם מאות בדיקות, הריצה לוקחת 5–10 דקות. כדי להאיץ, אנו משתמשים במקביליות. השוואה המבוססת על 500 בדיקות:

שיטה זמן הערה
ללא מקביליות ~5 דקות כל הבדיקות ברצף על runner אחד
Jest sharding (4 shards) ~1.5 דקות פיצול ל-4 משימות מקבילות
pytest-xdist (-n auto) ~2 דקות ספירת תהליכים אוטומטית לפי CPU
Go t.Parallel() ~1 דקה מקביליות מובנית ברמת הבדיקה

Jest sharding — אנו מפצלים את ערכת הבדיקות למספר משימות. מספר ה-shards נבחר כך שכל אחת רצה על runner נפרד. עבור פרויקט TypeScript עם 1000 בדיקות, זה הפחית את זמן הריצה מ-12 דקות ל-3.5.

pytest-xdist: - name: Run unit tests run: go test ./... -v -count=1 -coverprofile=coverage.out - name: Convert coverage run: go tool cover -func=coverage.out משתמש בכל הליבות הזמינות. על מונולית Python גדול, האיצנו את הריצה פי 4.

Go test: פשוט הוסף -count=1 בקוד הבדיקה וציין pytest -n auto tests/unit/.

תלות ב-caching היא קריטית: t.Parallel() עם -parallel 4 או actions/setup-node עם cache: 'npm' כל אחד חוסך 1–2 דקות לכל ריצה.

ההשפעה של אוטומציית בדיקות יחידה

מדד לפני אוטומציה אחרי אוטומציה
זמן להרצת 500 בדיקות 5–10 דקות (ידני) 1–2 דקות (אוטומטי + מקביליות)
מספר רגרסיות בחודש 3–5 0–1
זמן לסקירת קוד 30 דקות לכל PR 10 דקות לכל PR (בדיקות ירוקות)
אחוז כיסוי קוד לא ידוע 80%+ עם ספים

מה מספק Test Summary?

GitHub Actions תומך ב-Test Summary — טבלת תוצאות בדיקות ישירות בסיכום הצינור. הסוקר רואה: 47 עברו, 2 נכשלו — עם קישורים ישירים לבדיקות שנכשלו. מוגדר עם הפעולה הזו:

- name: Test Summary uses: test-summary/action@v2 if: always() with: paths: "reports/**/*.xml" 

אין יותר גלילה בלוגים כדי למצוא שגיאה.

מטריצת גרסאות

עבור ספריות, קריטי לבדוק מול מספר גרסאות runtime. זה תופס אי-תאימות שאחרת היו צצות רק אצל משתמשים. לדוגמה, בדיקה על 3 גרסאות Node.js תופסת 20% מאי-התאימות שגרסה אחת הייתה מפספסת.

strategy: matrix: node-version: ['18', '20', '22'] os: [ubuntu-latest, windows-latest] 

חסימת Merge על כישלון בדיקה

בהגדרות המאגר, אנו מוסיפים את המשימה actions/setup-python כ-Required Status Check. GitHub לא יאפשר מיזוג של ה-PR עד שהסטטוס ירוק. זו ערובה ארגונית — בלתי תלויה במשמעת המפתחים.

מה כלול בעבודה

  • ביקורת על CI/CD הנוכחי ותשתית הבדיקות;
  • כתיבה או שיפור של סקריפטים לצינור (YAML, Groovy, HCL);
  • שילוב caching ומקביליות;
  • הגדרת Test Summary ודוחות JUnit;
  • חיבור כיסוי קוד עם ערכי סף;
  • ניתוח בדיקות flaky והגדרת ניסיונות חוזרים אוטומטיים;
  • תיעוד צינור ו-runbook למפתחים;
  • הדרכת צוות (מפגש של שעה);
  • אחריות ביצועים ל-30 יום לאחר המסירה.

טעויות נפוצות בהגדרה

  1. חסר cache: 'pip' ב-Jest — snapshots מתעדכנים אוטומטית, שוברים בדיקות;
  2. שכחת העלאת ארטיפקטים של דוחות גם על כישלון (- name: Test Summary uses: test-summary/action@v2 if: always() with: paths: "reports/**/*.xml" );
  3. אי-השבתת caching של בדיקות ב-Go (השמטת strategy: matrix: node-version: ['18', '20', '22'] os: [ubuntu-latest, windows-latest] );
  4. שימוש ב-runner יחיד לכל המשימות — מקביליות לא עובדת ללא מטריצה;
  5. אי-הגדרת Required Status Checks — מיזוג אפשרי ללא בדיקות ירוקות.

הניסיון שלנו עוזר להימנע מבעיות אלה: בכל פרויקט אנו מתעדים runbook ומבצעים ניתוח postmortem. קבלו ייעוץ — כתבו לנו. צרו קשר כדי להעריך את הפרויקט שלכם — נכין הצעה ביום אחד.