testing-strategy

Comprehensive testing strategies and coverage standards. Use when testing strategy guidance is required.

$ インストール

git clone https://github.com/CsHeng/dot-claude /tmp/dot-claude && cp -r /tmp/dot-claude/skills/testing-strategy ~/.claude/skills/dot-claude

// tip: Run this command in your terminal to install the skill


name: testing-strategy description: Comprehensive testing strategies and coverage standards. Use when testing strategy guidance is required.

Purpose

Provide comprehensive testing strategies and coverage standards that can be applied across services, including thresholds, critical path tests, and environment setup.

IO Semantics

Input: Test suites, coverage reports, and service architectures that require structured testing guidance.

Output: Concrete coverage targets, configuration examples, and critical path testing patterns that can be enforced in CI.

Side Effects: Raising coverage thresholds or enforcing new critical paths may require additional tests and refactoring.

Deterministic Steps

1. Coverage Requirements Enforcement

Apply mandatory coverage thresholds:

  • Overall code coverage: ≥ 80%
  • Critical business logic coverage: ≥ 95%
  • Security-related code coverage: ≥ 90%
  • New feature coverage: ≥ 85% before merge

Apply coverage configuration examples:

# .pytest.ini for Python
[tool:pytest]
addopts =
    --cov=src
    --cov-report=term-missing
    --cov-report=html:htmlcov
    --cov-fail-under=80
    --cov-branch
    --cov-context=test

# Go coverage configuration
# Makefile
.PHONY: test test-coverage
test:
	go test -v ./...

test-coverage:
	go test -v -coverprofile=coverage.out ./...
	go tool cover -html=coverage.out -o coverage.html
	go tool cover -func=coverage.out | grep "total:" | awk '{print $3}' | sed 's/%//' | \
		awk '{if ($1 < 80) {print "Coverage below 80%: " $1 "%"; exit 1} else {print "Coverage: " $1 "%"}}'

2. Critical Path Testing

Identify and prioritize critical paths:

# test_critical_paths.py
import pytest
from unittest.mock import Mock, patch
from app.payment import PaymentProcessor
from app.user_management import UserService

class TestCriticalPaths:
    def test_payment_processing_complete_flow(self):
        """Test complete payment flow with real dependencies"""
        processor = PaymentProcessor()

        # Test successful payment
        result = processor.process_payment(
            user_id=123,
            amount=100.00,
            payment_method="credit_card"
        )

        assert result.success is True
        assert result.transaction_id is not None
        assert result.amount == 100.00

    def test_user_registration_with_validation(self):
        """Test user registration with all validation rules"""
        user_service = UserService()

        # Test valid registration
        user = user_service.register_user(
            email="test@example.com",
            password="SecurePass123!",
            name="Test User"
        )

        assert user.email == "test@example.com"
        assert user.is_active is True
        assert user.id is not None

    @pytest.mark.parametrize("status_code,expected_result", [
        (200, {"status": "success"}),
        (400, {"status": "error", "message": "Invalid request"}),
        (500, {"status": "error", "message": "Internal server error"})
    ])
    def test_api_endpoint_error_handling(self, status_code, expected_result):
        """Test API error handling scenarios"""
        with patch('requests.post') as mock_post:
            mock_post.return_value.status_code = status_code
            mock_post.return_value.json.return_value = expected_result

            response = self.client.call_external_api({"data": "test"})

            assert response == expected_result

Testing Framework Configuration

Multi-Language Testing Setup

Python Testing Configuration:

# pyproject.toml
[tool.pytest.ini_options]
minversion = "7.0"
addopts = [
    "--strict-markers",
    "--strict-config",
    "--cov=src",
    "--cov-report=term-missing",
    "--cov-report=html",
    "--cov-report=xml",
    "--cov-fail-under=80"
]
testpaths = ["tests"]
python_files = ["test_*.py", "*_test.py"]
python_classes = ["Test*"]
python_functions = ["test_*"]

markers = [
    "unit: Unit tests",
    "integration: Integration tests",
    "e2e: End-to-end tests",
    "slow: Slow running tests",
    "network: Tests requiring network access",
    "database: Tests requiring database"
]

Go Testing Configuration:

// testing_setup.go
package testsetup

import (
	"testing"
	"time"
)

// Test configuration
type TestConfig struct {
	DatabaseURL string
	RedisURL    string
	Timeout     time.Duration
}

// Global test configuration
var Config TestConfig

func TestMain(m *testing.M) {
	// Setup test environment
	setupTestEnvironment()

	// Run tests
	code := m.Run()

	// Cleanup
	cleanupTestEnvironment()

	os.Exit(code)
}

func setupTestEnvironment() {
	Config = TestConfig{
		DatabaseURL: "postgres://test:test@localhost:5432/testdb?sslmode=disable",
		RedisURL:    "redis://localhost:6379/1",
		Timeout:     30 * time.Second,
	}

	// Wait for services to be ready
	waitForServices()
}

Test Quality Assurance

AAA Pattern Implementation

Apply Arrange-Act-Assert consistently:

import pytest
from calculator import Calculator

class TestCalculator:
    def test_addition_positive_numbers(self):
        # Arrange
        calculator = Calculator()
        operand_a = 5
        operand_b = 3

        # Act
        result = calculator.add(operand_a, operand_b)

        # Assert
        assert result == 8
        assert isinstance(result, (int, float))

    def test_division_by_zero_raises_error(self):
        # Arrange
        calculator = Calculator()
        dividend = 10
        divisor = 0

        # Act & Assert
        with pytest.raises(ZeroDivisionError, match="Cannot divide by zero"):
            calculator.divide(dividend, divisor)

    def test_complex_calculation_chain(self):
        # Arrange
        calculator = Calculator()
        initial_value = 10

        # Act
        result = (calculator
                  .add(initial_value, 5)
                  .multiply(2)
                  .subtract(3)
                  .divide(4))

        # Assert
        assert result == 5.5

Test Isolation and Independence

Ensure tests run independently:

import pytest
import tempfile
import shutil
from pathlib import Path

class TestFileOperations:
    @pytest.fixture
    def temp_directory(self):
        """Create isolated temporary directory for each test"""
        temp_dir = tempfile.mkdtemp()
        yield Path(temp_dir)
        shutil.rmtree(temp_dir)

    def test_file_creation_and_read(self, temp_directory):
        """Test file operations in isolated environment"""
        test_file = temp_directory / "test.txt"
        test_content = "Hello, World!"

        # Act
        test_file.write_text(test_content)
        read_content = test_file.read_text()

        # Assert
        assert read_content == test_content
        assert test_file.exists()

    def test_file_operations_persistence(self, temp_directory):
        """This test uses different temp_directory, ensuring isolation"""
        # This test cannot interfere with previous test due to separate temp_directory
        another_file = temp_directory / "another.txt"
        another_file.write_text("Different content")

        assert another_file.read_text() == "Different content"

Performance and Load Testing

Load Testing Implementation

Automated performance validation:

import pytest
import time
import threading
from concurrent.futures import ThreadPoolExecutor
from app.api_server import app

class TestPerformance:
    @pytest.mark.slow
    def test_api_response_time_under_load(self):
        """Test API response time under concurrent load"""
        url = "http://localhost:8000/api/test"
        concurrent_requests = 50
        max_response_time = 1.0  # seconds

        def make_request():
            start_time = time.time()
            response = requests.get(url)
            end_time = time.time()
            return end_time - start_time, response.status_code

        with ThreadPoolExecutor(max_workers=concurrent_requests) as executor:
            futures = [executor.submit(make_request) for _ in range(concurrent_requests)]
            results = [future.result() for future in futures]

        # Assert all requests succeeded
        status_codes = [result[1] for result in results]
        assert all(code == 200 for code in status_codes)

        # Assert response times are within limits
        response_times = [result[0] for result in results]
        assert max(response_times) < max_response_time
        assert sum(response_times) / len(response_times) < max_response_time * 0.8

    @pytest.mark.performance
    def test_memory_usage_stability(self):
        """Test memory usage remains stable during extended operation"""
        import psutil
        import os

        process = psutil.Process(os.getpid())
        initial_memory = process.memory_info().rss

        # Perform many operations
        for _ in range(1000):
            self.heavy_operation()

        final_memory = process.memory_info().rss
        memory_increase = final_memory - initial_memory

        # Memory increase is minimal (< 10MB)
        assert memory_increase < 10 * 1024 * 1024, f"Memory increased by {memory_increase} bytes"

    def heavy_operation(self):
        """Simulate memory-intensive operation"""
        data = list(range(1000))
        processed_data = [x * 2 for x in data]
        return sum(processed_data)

Integration and E2E Testing

Integration Test Architecture

Comprehensive integration testing:

import pytest
from testcontainers.postgres import PostgresContainer
from testcontainers.redis import RedisContainer
from app.database import DatabaseConnection
from app.cache import RedisCache

@pytest.mark.integration
class TestDatabaseIntegration:
    @pytest.fixture(scope="class")
    def postgres_container(self):
        """Start PostgreSQL container for integration tests"""
        with PostgresContainer("postgres:15-alpine") as postgres:
            yield postgres

    @pytest.fixture(scope="class")
    def redis_container(self):
        """Start Redis container for integration tests"""
        with RedisContainer("redis:7-alpine") as redis:
            yield redis

    @pytest.fixture(scope="class")
    def database(self, postgres_container):
        """Configure database connection with test container"""
        db = DatabaseConnection(
            host=postgres_container.get_container_host_ip(),
            port=postgres_container.get_exposed_port(5432),
            database="test",
            user="test",
            password="test"
        )
        db.create_tables()
        yield db
        db.close()

    def test_user_creation_and_retrieval(self, database):
        """Test complete user creation and retrieval flow"""
        user_data = {
            "email": "test@example.com",
            "name": "Test User"
        }

        # Create user
        user_id = database.create_user(user_data)
        assert user_id is not None

        # Retrieve user
        retrieved_user = database.get_user(user_id)
        assert retrieved_user["email"] == user_data["email"]
        assert retrieved_user["name"] == user_data["name"]

    def test_transaction_rollback(self, database):
        """Test transaction rollback on error"""
        initial_count = database.count_users()

        with pytest.raises(ValueError):
            with database.transaction():
                database.create_user({"email": "good@example.com", "name": "Good User"})
                raise ValueError("Simulated error")

        # Ensure no user was created due to rollback
        final_count = database.count_users()
        assert final_count == initial_count

End-to-End Testing

Full application workflow testing:

import pytest
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

@pytest.mark.e2e
class TestUserWorkflows:
    @pytest.fixture
    def browser(self):
        """Setup browser for E2E tests"""
        options = webdriver.ChromeOptions()
        options.add_argument('--headless')
        options.add_argument('--no-sandbox')
        options.add_argument('--disable-dev-shm-usage')

        driver = webdriver.Chrome(options=options)
        driver.implicitly_wait(10)

        yield driver

        driver.quit()

    def test_complete_user_registration_flow(self, browser):
        """Test complete user registration from UI"""
        # Navigate to registration page
        browser.get("http://localhost:3000/register")

        # Fill registration form
        browser.find_element(By.ID, "email").send_keys("test@example.com")
        browser.find_element(By.ID, "password").send_keys("SecurePass123!")
        browser.find_element(By.ID, "confirm_password").send_keys("SecurePass123!")
        browser.find_element(By.ID, "name").send_keys("Test User")

        # Submit form
        browser.find_element(By.ID, "register-button").click()

        # Verify successful registration
        WebDriverWait(browser, 10).until(
            EC.presence_of_element_located((By.CLASS_NAME, "success-message"))
        )

        # Verify redirect to dashboard
        assert "dashboard" in browser.current_url

    def test_login_with_registered_user(self, browser):
        """Test login with previously registered user"""
        # First register a user
        self.register_test_user(browser)

        # Navigate to login page
        browser.get("http://localhost:3000/login")

        # Fill login form
        browser.find_element(By.ID, "email").send_keys("test@example.com")
        browser.find_element(By.ID, "password").send_keys("SecurePass123!")
        browser.find_element(By.ID, "login-button").click()

        # Verify successful login
        WebDriverWait(browser, 10).until(
            EC.presence_of_element_located((By.CLASS_NAME, "user-dashboard"))
        )

    def register_test_user(self, browser):
        """Helper method to register test user"""
        browser.get("http://localhost:3000/register")
        browser.find_element(By.ID, "email").send_keys("test@example.com")
        browser.find_element(By.ID, "password").send_keys("SecurePass123!")
        browser.find_element(By.ID, "confirm_password").send_keys("SecurePass123!")
        browser.find_element(By.ID, "name").send_keys("Test User")
        browser.find_element(By.ID, "register-button").click()
        WebDriverWait(browser, 10).until(
            EC.presence_of_element_located((By.CLASS_NAME, "success-message"))
        )

Continuous Integration Testing

Automated Test Pipeline

Comprehensive CI/CD testing workflow:

# .github/workflows/test.yml
name: Test Suite

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest

    services:
      postgres:
        image: postgres:15
        env:
          POSTGRES_PASSWORD: test
          POSTGRES_DB: testdb
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5

      redis:
        image: redis:7
        options: >-
          --health-cmd "redis-cli ping"
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5

    steps:
      - uses: actions/checkout@v3

      - name: Set up Python
        uses: actions/setup-python@v4
        with:
          python-version: '3.11'

      - name: Set up Go
        uses: actions/setup-go@v4
        with:
          go-version: '1.21'

      - name: Install dependencies
        run: |
          pip install -r requirements.txt
          pip install -r requirements-dev.txt

      - name: Run Python unit tests
        run: |
          pytest tests/unit/ -v \
            --cov=src \
            --cov-report=xml \
            --cov-fail-under=80

      - name: Run Python integration tests
        run: |
          pytest tests/integration/ -v \
            --cov=src \
            --cov-append \
            --cov-report=xml

      - name: Run Go tests
        run: |
          go test -v -race -coverprofile=coverage.out ./...
          go tool cover -html=coverage.out -o coverage.html

      - name: Run E2E tests
        run: |
          docker-compose -f docker-compose.test.yml up -d
          sleep 30
          pytest tests/e2e/ -v
          docker-compose -f docker-compose.test.yml down

      - name: Upload coverage reports
        uses: codecov/codecov-action@v3
        with:
          files: ./coverage.xml,coverage.out

  security-scan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3

      - name: Run security scan
        run: |
          pip install bandit safety
          bandit -r src/ -f json -o bandit-report.json
          safety check --json --output safety-report.json

      - name: Upload security reports
        uses: actions/upload-artifact@v3
        with:
          name: security-reports
          path: |
            bandit-report.json
            safety-report.json