算法艺术创作
algorithmic-art
通过代码和可调参数创作生成艺术与交互式视觉效果。
quality-unit-testing【设计与创意】Use when the user asks about unit testing, test doubles, mocking, stubbing, test-driven development (TDD), FIRST principles, code coverage, test structure (AAA), or unit test patterns. Do NOT use for: integration testing (quality-integration-testing), E2E testing (quality-e2e-testing), or frontend testing (frontend-testing).
独立 GitHub 快照 · 自动分类 · 以原作者文档为准
Write effective unit tests using FIRST principles, AAA pattern, test doubles, and TDD. Ensure test quality, maintainability, and meaningful coverage of business logic. This skill covers test architecture, isolation strategies, mock/stub decisions, CI integration, and test suite health monitoring.
User mentions unit testing, test doubles, mocking, stubbing, TDD, FIRST principles, code coverage, test structure (AAA), or unit test patterns.
Unit test suite with proper isolation, test doubles, and CI integration.
Test file(s) with:
Dependency type?
├── External HTTP/network → Mock (MSW/WireMock)
├── Database → Fake (in-memory repo) or Mock at repository boundary
├── File system → Mock (memfs) or in-memory implementation
├── Time (Date, setTimeout) → Fake timers (vi.useFakeTimers)
├── Random/UUID → Stub with fixed values
├── Logger → Stub (no-op or spy)
├── Pure function → Real implementation
└── Internal service → Real implementation or Spy
Project language?
├── TypeScript/JavaScript
│ ├── New project + ESM → Vitest
│ └── Existing Jest → Jest
├── Java → JUnit 5 + Mockito
├── Python → pytest + pytest-mock
├── Go → testing + testify
├── Rust → cargo test
└── C# → xUnit + Moq/NSubstitute
| Aspect | Unit Testing | Integration Testing | E2E Testing |
|---|---|---|---|
| Scope | Single function/module | Component interactions | Full user workflows |
| Isolation | Full (mocked deps) | Partial (real deps) | None (real system) |
| Speed | Milliseconds | Seconds | Minutes |
| Debugging | Easy (isolated) | Medium | Hard |
| Brittleness | Low | Medium | High |
| Confidence | Low (isolated) | Medium | High |
| When | Every commit | On feature completion | Pre-release |
mockReset and restoreMocks for automatic cleanupit.concurrent for independent tests within the same describe block// src/services/order.service.ts
export class OrderService {
constructor(
private orderRepo: OrderRepository,
private paymentGateway: PaymentGateway,
private emailService: EmailService,
) {}
async placeOrder(cart: Cart, customerId: string): Promise<Order> {
const total = cart.items.reduce((sum, item) => sum + item.price, 0);
const payment = await this.paymentGateway.charge(customerId, total);
const order = await this.orderRepo.create({ customerId, total, paymentId: payment.id });
await this.emailService.sendOrderConfirmation(customerId, order.id);
return order;
}
}
// src/services/__tests__/order.service.test.ts
import { describe, it, expect, vi, beforeEach } from "vitest";
import { OrderService } from "../order.service";
describe("OrderService", () => {
let service: OrderService;
let mockOrderRepo: OrderRepository;
let mockPaymentGateway: PaymentGateway;
let mockEmailService: EmailService;
beforeEach(() => {
mockOrderRepo = { create: vi.fn() };
mockPaymentGateway = { charge: vi.fn() };
mockEmailService = { sendOrderConfirmation: vi.fn() };
service = new OrderService(mockOrderRepo, mockPaymentGateway, mockEmailService);
});
it("should create order and charge payment when placing order", async () => {
const cart = { items: [{ price: 50 }, { price: 30 }] };
mockPaymentGateway.charge.mockResolvedValue({ id: "pay_123" });
mockOrderRepo.create.mockResolvedValue({ id: "ord_456" });
const result = await service.placeOrder(cart, "cust_789");
expect(mockPaymentGateway.charge).toHaveBeenCalledWith("cust_789", 80);
expect(mockOrderRepo.create).toHaveBeenCalledWith({
customerId: "cust_789",
total: 80,
paymentId: "pay_123",
});
expect(mockEmailService.sendOrderConfirmation).toHaveBeenCalledWith("cust_789", "ord_456");
expect(result).toEqual({ id: "ord_456" });
});
it("should not create order when payment fails", async () => {
const cart = { items: [{ price: 50 }] };
mockPaymentGateway.charge.mockRejectedValue(new Error("insufficient_funds"));
await expect(service.placeOrder(cart, "cust_789")).rejects.toThrow("insufficient_funds");
expect(mockOrderRepo.create).not.toHaveBeenCalled();
expect(mockEmailService.sendOrderConfirmation).not.toHaveBeenCalled();
});
});
# src/pricing.py
from dataclasses import dataclass
from decimal import Decimal
@dataclass
class PriceBreak:
quantity: int
discount_percent: Decimal
def calculate_discount(quantity: int, breaks: list[PriceBreak]) -> Decimal:
applicable = [b for b in breaks if quantity >= b.quantity]
if not applicable:
return Decimal("0")
return max(applicable, key=lambda b: b.discount_percent).discount_percent
def calculate_total(items: list[dict], discount_pct: Decimal) -> Decimal:
subtotal = sum(Decimal(str(item["price"])) * item["quantity"] for item in items)
discount = subtotal * (discount_pct / Decimal("100"))
return subtotal - discount
# tests/test_pricing.py
import pytest
from decimal import Decimal
from src.pricing import calculate_discount, calculate_total, PriceBreak
class TestCalculateDiscount:
def test_no_breaks_returns_zero(self):
assert calculate_discount(10, []) == Decimal("0")
def test_quantity_meets_single_break(self):
breaks = [PriceBreak(5, Decimal("10"))]
assert calculate_discount(5, breaks) == Decimal("10")
def test_quantity_exceeds_break(self):
breaks = [PriceBreak(5, Decimal("10"))]
assert calculate_discount(10, breaks) == Decimal("10")
def test_highest_applicable_break_wins(self):
breaks = [
PriceBreak(5, Decimal("10")),
PriceBreak(10, Decimal("15")),
PriceBreak(20, Decimal("20")),
]
assert calculate_discount(15, breaks) == Decimal("15")
assert calculate_discount(25, breaks) == Decimal("20")
def test_quantity_below_all_breaks_returns_zero(self):
breaks = [PriceBreak(5, Decimal("10"))]
assert calculate_discount(3, breaks) == Decimal("0")
# tests/test_order_service.py
from unittest.mock import Mock, patch
from src.order_service import OrderService
class TestOrderService:
@patch("src.order_service.EmailService")
@patch("src.order_service.PaymentGateway")
@patch("src.order_service.OrderRepository")
def test_place_order_success(
self, mock_repo_cls, mock_payment_cls, mock_email_cls
):
mock_repo = Mock()
mock_payment = Mock()
mock_email = Mock()
mock_repo_cls.return_value = mock_repo
mock_payment_cls.return_value = mock_payment
mock_email_cls.return_value = mock_email
service = OrderService()
mock_payment.charge.return_value = {"id": "pay_123"}
mock_repo.create.return_value = {"id": "ord_456"}
cart = {"items": [{"price": "50.00", "quantity": 1}]}
result = service.place_order(cart, "cust_789")
assert result["id"] == "ord_456"
mock_payment.charge.assert_called_once()
mock_repo.create.assert_called_once()
mock_email.send_confirmation.assert_called_once()
name: Unit Tests
on: pull_request
jobs:
unit:
runs-on: ubuntu-latest
strategy:
matrix:
shard: [1, 2, 3, 4]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- run: npm ci
- run: npx vitest run --reporter=junit --shard=${{ matrix.shard }}/4
- uses: actions/upload-artifact@v4
if: always()
with:
name: coverage-${{ matrix.shard }}
path: coverage/
coverage:
needs: unit
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm ci
- run: npx vitest run --coverage
- uses: davelosert/vitest-coverage-report-action@v2
with:
json-summary-path: coverage/coverage-summary.json
Mocking every dependency including pure functions and internal collaborators creates brittle tests that break during refactoring. Only mock at system boundaries (network, filesystem, time, RNG). Use real implementations for value objects, pure functions, and internal helpers.
Testing private methods directly couples tests to implementation details. Private methods change during refactoring while public behavior stays the same. Test through the public interface only. If a private method has complex logic, extract it to a separate module and test it as a public function.
Tests that share mutable state through module-level variables or test fixtures with side effects produce order-dependent failures. Each test must be independently runnable. Use beforeEach to create fresh instances. Never modify shared state in a test.
Asserting that a specific method was called internally or that a specific property exists on an object. These assertions break during refactoring. Assert only on observable behavior: return values, thrown exceptions, and side effects visible to the caller.
Tests that call a function but don't assert anything, or assert tautologies like expect(true).toBe(true). Every test must assert at least one meaningful outcome. If a test passes without any real assertion, it provides false confidence.
Testing only the happy path (correct input, expected behavior) misses null/undefined inputs, boundary values, empty collections, and error conditions. Every function should have tests for: normal input, boundary values, invalid input, empty/null input, and error conditions.
| Level | Characteristics | Practices |
|---|---|---|
| 1: Initial | No or minimal unit tests | Manual testing only, no test framework, no CI integration |
| 2: Defined | Basic coverage for critical logic | Tests for core business logic, basic mock usage, some CI integration |
| 3: Managed | Systematic unit testing | AAA pattern, proper test doubles at boundaries, >70% coverage, CI gates, test naming conventions |
| 4: Measured | Test-driven quality culture | TDD for complex logic, property-based testing supplement, <100ms per test, flakiness < 0.1% |
| 5: Optimized | Self-healing test suite | Mutation testing for quality validation, AI-assisted test generation, automatic test maintenance, coverage-based risk assessment |
should return sorted array when called with unsorted inputAfter unit testing, hand off to:
quality-integration-testing — for verifying component interactions with real dependenciesquality-property-based-testing — for adding property-based invariants to complement examplesquality-regression-testing — for regression suite execution and maintenancequality-smoke-testing — for BVT smoke test definition on tested components` interface EventObserver { onEvent(event: T): Promise; }
class EventBus { private observers: Set<EventObserver> = new Set(); subscribe(observer: EventObserver): void { this.observers.add(observer); } unsubscribe(observer: EventObserver): void { this.observers.delete(observer); } async emit(event: T): Promise { const results = Array.from(this.observers).map(o => o.onEvent(event)); await Promise.allSettled(results); } } `
config: defaults: timeout: 30s retryCount: 3 overrides: production: timeout: 60s retryCount: 5 development: timeout: 300s retryCount: 1
| Metric | Threshold | Severity | Action |
|---|---|---|---|
| Error rate | > 1% over 5min | Critical | Page on-call |
| p99 latency | > 2s over 5min | Warning | Investigate |
| Throughput drop | > 50% over 1min | Critical | Check upstream |
| Queue depth | > 1000 over 1min | Warning | Scale consumers |
| Disk usage | > 85% | Warning | Clean or expand |
| Memory usage | > 90% heap | Critical | Restart or scale |
| Anti-Pattern | Symptom | Root Cause | Solution |
|---|---|---|---|
| Premature optimization | Complex code for no measured benefit | Guessing instead of profiling | Measure first, optimize based on data |
| Copy-paste reuse | Duplicate code across codebase | Lack of abstraction | Extract shared logic into libraries |
| Gold-plating | Features with no current requirement | Over-engineering | YAGNI — build what's needed now |
| Magical thinking | Assumptions without validation | Skipping error handling | Handle all failure modes explicitly |
Cache hierarchy: L1 (in-memory local) → L2 (distributed Redis/Memcached) → L3 (CDN/Edge). Cache invalidation: TTL-based (simple, stale), event-based (complex, fresh), write-through (consistent, higher write latency), write-behind (fast writes, eventual consistency).
| Decision Point | Option A | Option B | Decision Criteria |
|---|---|---|---|
| Test framework | Jest (JS/TS ecosystem) | Vitest (faster, ESM-native) | Framework maturity, ESM compatibility |
| Assertion style | expect(x).toBe(y) (readable) | assert.equal(x, y) (minimal) | Team preference, existing codebase |
| Mocking style | Jest mocks (built-in) | ts-mockito/typed-mock | Type safety, TypeScript usage |
| Coverage tool | Built-in (Jest Istanbul) | c8 (modern, ESM-friendly) | Coverage needs, configuration complexity |