Free

הרצת בדיקות אוטומטית עם Hooks: Claude יודע מיד אם הקוד שלו שבר משהו

השתמשו ב-PostToolUse Hooks על כלי Write/Edit להרצת בדיקות אחרי כל שינוי — כשלונות מוחזרים ל-Claude לתיקון מיידי.


Claude Code עורך קוד מהר — אבל לא מריץ בדיקות בעצמו. כברירת מחדל, ברגע שכתב קובץ, המשימה נחשבת לגמורה. בלי הוראה מפורשת, הוא לא יודע אם השינוי שבר משהו.

Hook מסוג PostToolUse פותר את זה: בכל פעם ש-Claude כותב קובץ, הבדיקות רצות אוטומטית. כשנכשלות, התוצאה מוחזרת ישירות ל-Claude שמתקן מיד.


איך זה עובד

Claude Code משנה קבצים עם שני כלים: Write (יצירה או החלפה) ו-Edit (שינויים חלקיים). חיבור Hook לשלב PostToolUse של כלים אלה מפעיל הרצת בדיקות בכל פעם שקובץ נשמר לדיסק.

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "bash .claude/hooks/run-tests.sh"
          }
        ]
      }
    ]
  }
}

ה-Hook מקבל את ה-JSON המלא של קריאת הכלי דרך stdin, כולל הנתיב של הקובץ שהשתנה. שימוש בנתיב זה להחלטה אילו בדיקות להריץ הוא מפתח הגישה.

כללי קוד יציאה (PostToolUse):
- exit 0: בדיקות עברו — Claude ממשיך ללא הפרעה
- exit 2: בדיקות נכשלו — תוכן stderr מוזרק ל-Claude שרואה את השגיאה ומנסה לתקן


גרסה בסיסית: בחירת פקודת הבדיקה לפי סוג הקובץ

הגישה הפשוטה ביותר: לבדוק את סיומת הקובץ ולהריץ את חבילת הבדיקות המתאימה.

#!/bin/bash
input=$(cat)
file=$(echo "$input" | jq -r '.tool_input.file_path // .tool_input.path // empty')

[[ -z "$file" ]] && exit 0

# לא להריץ בדיקות כשקלוד עורך קובץ בדיקה עצמו
echo "$file" | grep -qE '(test|spec)\.' && exit 0

case "$file" in
  *.py)
    cd "$(git rev-parse --show-toplevel 2>/dev/null)" || exit 0
    result=$(python -m pytest --tb=short -q 2>&1)
    ;;
  *.ts|*.js|*.tsx|*.jsx)
    cd "$(git rev-parse --show-toplevel 2>/dev/null)" || exit 0
    result=$(npm test -- --passWithNoTests 2>&1)
    ;;
  *.go)
    dir=$(dirname "$file")
    result=$(go test "./$dir/..." 2>&1)
    ;;
  *.rb)
    cd "$(git rev-parse --show-toplevel 2>/dev/null)" || exit 0
    result=$(bundle exec rspec --format progress 2>&1)
    ;;
  *)
    exit 0
    ;;
esac

exit_code=$?

if [[ $exit_code -ne 0 ]]; then
  echo "בדיקות נכשלו (קובץ שהשתנה: $file):" >&2
  echo "$result" >&2
  exit 2
fi

גרסה מתקדמת: מיקוד בקובץ הבדיקה הספציפי

להריץ את כל החבילה בכל שינוי — איטי מדי. השתמשו בנתיב הקובץ שהשתנה כדי למצוא רק את הבדיקות הרלוונטיות.

בפרויקט Python, src/auth/login.py בדרך כלל ממופה ל-tests/auth/test_login.py:

#!/bin/bash
input=$(cat)
file=$(echo "$input" | jq -r '.tool_input.file_path // .tool_input.path // empty')

[[ -z "$file" || "$file" != *.py ]] && exit 0
echo "$file" | grep -qE '(test_|_test\.py)' && exit 0

root=$(git rev-parse --show-toplevel 2>/dev/null) || exit 0
cd "$root"

basename=$(basename "$file" .py)
test_file=$(find tests -name "test_${basename}.py" 2>/dev/null | head -1)

if [[ -n "$test_file" ]]; then
  result=$(python -m pytest "$test_file" --tb=short -q 2>&1)
else
  result=$(python -m pytest --tb=short -q 2>&1)
fi

if [[ $? -ne 0 ]]; then
  echo "בדיקות נכשלו:" >&2
  echo "$result" >&2
  exit 2
fi

עבור JavaScript/TypeScript, Jest תומך בסינון לפי תבנית שם קובץ:

#!/bin/bash
input=$(cat)
file=$(echo "$input" | jq -r '.tool_input.file_path // .tool_input.path // empty')

[[ -z "$file" ]] && exit 0
echo "$file" | grep -qE '\.(test|spec)\.' && exit 0
echo "$file" | grep -qE '\.(ts|tsx|js|jsx)$' || exit 0

root=$(git rev-parse --show-toplevel 2>/dev/null) || exit 0
cd "$root"

basename=$(basename "$file" | sed 's/\.[^.]*$//')
result=$(npx jest --testPathPattern="$basename" --passWithNoTests 2>&1)

if [[ $? -ne 0 ]]; then
  echo "בדיקות נכשלו:" >&2
  echo "$result" >&2
  exit 2
fi

קונפיגורציה מלאה

לכסות גם Write וגם Edit, עם הגנת timeout למניעת בדיקות תקועות שיחסמו את Claude:

.claude/hooks/run-tests.sh:

#!/bin/bash
set -euo pipefail

input=$(cat)
file=$(echo "$input" | jq -r '.tool_input.file_path // .tool_input.path // empty')

[[ -z "$file" ]] && exit 0

# לדלג על קבצי בדיקה עצמם וקבצים שאינם קוד
echo "$file" | grep -qE '(test_|_test\.|\.test\.|\.spec\.)' && exit 0
echo "$file" | grep -qE '\.(py|ts|js|tsx|jsx|go|rb)$' || exit 0

root=$(git rev-parse --show-toplevel 2>/dev/null) || exit 0
cd "$root"

run_test() {
  timeout 60 "$@" 2>&1
}

case "$file" in
  *.py)
    result=$(run_test python -m pytest --tb=short -q)
    ;;
  *.ts|*.tsx|*.js|*.jsx)
    basename=$(basename "$file" | sed 's/\.[^.]*$//')
    result=$(run_test npx jest --testPathPattern="$basename" --passWithNoTests)
    ;;
  *.go)
    dir=$(dirname "$file")
    result=$(run_test go test "./$dir/...")
    ;;
  *.rb)
    result=$(run_test bundle exec rspec --format progress)
    ;;
  *)
    exit 0
    ;;
esac

if [[ $? -ne 0 ]]; then
  echo "⚠ בדיקות נכשלו (קובץ מפעיל: $(basename "$file"))" >&2
  echo "" >&2
  echo "$result" >&2
  exit 2
fi

.claude/settings.json:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "bash .claude/hooks/run-tests.sh"
          }
        ]
      }
    ]
  }
}

דברים חשובים לדעת

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

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

echo "$file" | grep -qE '(test_|_test\.|\.test\.|\.spec\.)' && exit 0

Write ו-Edit משתמשים בשמות שדות שונים ב-tool_input

Write משתמש ב-file_path, Edit ב-path. לטפל בשניהם עם fallback:

file=$(echo "$input" | jq -r '.tool_input.file_path // .tool_input.path // empty')

לשלוט ב-timeout של הבדיקות

חבילות בדיקה איטיות בשילוב עם עריכות תכופות יכולות להקפיא את הסשן. timeout 60 הוא ברירת מחדל סבירה; לכוונן לפי משך הבדיקה האמיתי של הפרויקט.

רמת פרויקט, לא גלובלית

Hook זה שייך לקונפיגורציה ברמת פרויקט (.claude/settings.json). פקודות הבדיקה משתנות מדי בין פרויקטים. לגרסה גלובלית, הסקריפט צריך לזהות את סוג הפרויקט באופן דינמי.


התוצאה

עם הקונפיגורציה הזו, קצב העבודה של Claude הופך ל:

  1. עורך קובץ מקור
  2. ה-Hook מריץ את הבדיקות הרלוונטיות אוטומטית
  3. בדיקות נכשלות → Claude רואה את פלט השגיאות וממשיך לערוך עד שעוברות
  4. בדיקות עוברות → שקט, Claude עובר לשלב הבא

אין יותר צורך לחזור ולומר "הרץ את הבדיקות". לכל שינוי של Claude יש רשת ביטחון של בדיקות.