Mutation testing
Skonfiguruj zaawansowane mutation testing
Budujesz silnik obliczeń finansowych dla platformy inwestycyjnej. Wymagania obejmują obliczenia odsetek składanych, algorytmy rebalansowania portfela, strategie optymalizacji podatkowej i ocenę ryzyka. Obliczenia muszą być w 100% dokładne, obsługiwać przypadki brzegowe i być dokładnie udokumentowane. Twój zespół wymaga podejścia TDD z >95% pokryciem testami.
Po ukończeniu tej lekcji opanujesz:
Zbuduj silnik obliczeń używając czystego TDD:
Zacznij od analizy wymagań:
"Muszę zbudować silnik obliczeń finansowych z tymi funkcjami:1. Kalkulator odsetek składanych2. Rebalansowanie portfela3. Tax loss harvesting4. Ocena ryzyka (wskaźnik Sharpe'a, itd.)
Pomóż mi stworzyć kompleksowy plan testów zgodnie z zasadami TDD.Jakie kategorie testów i scenariusze powinienem rozważyć?"
"Zaprojektuj strukturę plików testowych dla:- Testów jednostkowych każdego obliczenia- Testów integracji dla workflow- Testów opartych na właściwościach dla właściwości matematycznych- Testów wydajności dla dużych portfeli- Testów snapshot dla raportówStwórz strukturę katalogów i szablony testów"
Przełącz się na tryb Agent:
"Stwórz narzędzia i helpery testowe:- Fabryki danych testowych dla portfeli- Helpery asercji dla wartości finansowych- Generatory mock danych- Custom matchery dla precyzji- Fixtures testowe dla typowych scenariuszy"
Przykład narzędzi testowych:
export const expectFinanciallyEqual = ( actual: number, expected: number, precision: number = 2) => { const multiplier = Math.pow(10, precision); expect(Math.round(actual * multiplier)).toBe( Math.round(expected * multiplier) );};
export const createPortfolio = (overrides?: Partial<Portfolio>): Portfolio => ({ id: faker.string.uuid(), name: faker.company.name() + ' Portfolio', holdings: [ { symbol: 'AAPL', shares: faker.number.int({ min: 10, max: 1000 }), costBasis: faker.number.float({ min: 100, max: 200 }), }, { symbol: 'GOOGL', shares: faker.number.int({ min: 5, max: 500 }), costBasis: faker.number.float({ min: 1000, max: 3000 }), }, ], cash: faker.number.float({ min: 1000, max: 100000 }), ...overrides,});
export const createMarketData = (): MarketData => ({ AAPL: { price: 150.00, change: 0.02 }, GOOGL: { price: 2800.00, change: -0.01 }, // ... więcej symboli});
"Napisz kompleksowe testy dla obliczenia odsetek składanych:- Podstawowe obliczenie z rocznym składaniem- Różne częstotliwości składania (miesięczne, dzienne, ciągłe)- Przypadki brzegowe (ujemne stopy, zero kapitału, wartości ekstremalne)- Zachowanie precyzji i zaokrąglania- Obsługa nieprawidłowych wejśćDołącz wzór matematyczny w komentarzach"
Przykład zestawu testów:
describe('CompoundInterestCalculator', () => { describe('calculate()', () => { // Wzór: A = P(1 + r/n)^(nt) // A = kwota końcowa, P = kapitał, r = stopa, n = składania na rok, t = czas
it('powinno obliczyć proste roczne odsetki składane', () => { const result = calculateCompoundInterest({ principal: 1000, rate: 0.05, time: 10, compoundingFrequency: 1, });
// 1000 * (1 + 0.05/1)^(1*10) = 1628.89 expectFinanciallyEqual(result.finalAmount, 1628.89); expectFinanciallyEqual(result.totalInterest, 628.89); });
it('powinno obsłużyć składanie miesięczne', () => { const result = calculateCompoundInterest({ principal: 1000, rate: 0.05, time: 10, compoundingFrequency: 12, });
// 1000 * (1 + 0.05/12)^(12*10) = 1647.01 expectFinanciallyEqual(result.finalAmount, 1647.01); });
it('powinno obsłużyć składanie ciągłe', () => { const result = calculateCompoundInterest({ principal: 1000, rate: 0.05, time: 10, compoundingFrequency: 'continuous', });
// P * e^(rt) = 1000 * e^(0.05*10) = 1648.72 expectFinanciallyEqual(result.finalAmount, 1648.72); });
describe('przypadki brzegowe', () => { it('powinno obsłużyć zero kapitału', () => { const result = calculateCompoundInterest({ principal: 0, rate: 0.05, time: 10, }); expect(result.finalAmount).toBe(0); });
it('powinno obsłużyć ujemne stopy procentowe', () => { const result = calculateCompoundInterest({ principal: 1000, rate: -0.02, time: 5, }); expectFinanciallyEqual(result.finalAmount, 904.84); });
it('powinno obsłużyć bardzo duże liczby bez przepełnienia', () => { const result = calculateCompoundInterest({ principal: 1e10, rate: 0.15, time: 50, }); expect(result.finalAmount).toBeGreaterThan(0); expect(result.finalAmount).toBeLessThan(Infinity); }); });
describe('walidacja', () => { it('powinno rzucić dla nieprawidłowego kapitału', () => { expect(() => calculateCompoundInterest({ principal: NaN, rate: 0.05, time: 10, }) ).toThrow('Kapitał musi być prawidłową liczbą'); });
it('powinno rzucić dla nieprawidłowego okresu czasu', () => { expect(() => calculateCompoundInterest({ principal: 1000, rate: 0.05, time: -5, }) ).toThrow('Okres czasu musi być dodatni'); }); }); });});
@test-utils/financial.ts"Stwórz testy dla algorytmu rebalansowania portfela:- Rebalansowanie alokacji docelowej- Rebalansowanie oparte na progach- Rebalansowanie uwzględniające podatki- Minimalizacja kosztów transakcji- Przypadki brzegowe (niepłynne aktywa, ograniczenia)Testuj zarówno algorytm jak i jego optymalizacje"
"Napisz testy dla metryk ryzyka:- Obliczenie wskaźnika Sharpe'a- Odchylenie standardowe zwrotów- Maksymalny drawdown- Value at Risk (VaR)- Obliczenie betaDołącz testy dla różnych okresów czasu i jakości danych"
"Stwórz testy oparte na właściwościach dla obliczeń finansowych:- Właściwości matematyczne (łączność, rozdzielność)- Niezmienniki (wartość portfela = suma holdingów + gotówka)- Granice (zwroty między -100% a +∞)- Monotoniczność (więcej czasu = więcej odsetek składanych)Użyj biblioteki fast-check"
Przykład testów właściwości:
import fc from 'fast-check';
describe('Właściwości obliczeń finansowych', () => { describe('Właściwości odsetek składanych', () => { it('powinno zawsze rosnąć z dodatnią stopą i czasem', () => { fc.assert( fc.property( fc.float({ min: 100, max: 1000000 }), // kapitał fc.float({ min: 0.001, max: 0.20 }), // stopa fc.integer({ min: 1, max: 50 }), // lata (principal, rate, time) => { const result = calculateCompoundInterest({ principal, rate, time, });
expect(result.finalAmount).toBeGreaterThan(principal); expect(result.totalInterest).toBeGreaterThan(0); } ) ); });
it('powinno być monotoniczne względem czasu', () => { fc.assert( fc.property( fc.float({ min: 100, max: 1000000 }), fc.float({ min: 0.001, max: 0.20 }), fc.integer({ min: 1, max: 30 }), fc.integer({ min: 1, max: 20 }), (principal, rate, time1, extraTime) => { const time2 = time1 + extraTime;
const result1 = calculateCompoundInterest({ principal, rate, time: time1 }); const result2 = calculateCompoundInterest({ principal, rate, time: time2 });
expect(result2.finalAmount).toBeGreaterThan(result1.finalAmount); } ) ); }); });
describe('Właściwości portfela', () => { it('powinno zachować całkowitą wartość przez rebalansowanie', () => { fc.assert( fc.property( fc.array( fc.record({ symbol: fc.string({ minLength: 1, maxLength: 5 }), shares: fc.integer({ min: 1, max: 1000 }), price: fc.float({ min: 1, max: 5000 }), }), { minLength: 2, maxLength: 10 } ), fc.float({ min: 0, max: 100000 }), (holdings, cash) => { const portfolio = { holdings, cash }; const totalBefore = calculatePortfolioValue(portfolio);
const rebalanced = rebalancePortfolio(portfolio, { STOCKS: 0.6, BONDS: 0.3, CASH: 0.1, });
const totalAfter = calculatePortfolioValue(rebalanced);
// Uwzględnij koszty transakcji expect(totalAfter).toBeGreaterThanOrEqual(totalBefore * 0.99); expect(totalAfter).toBeLessThanOrEqual(totalBefore); } ) ); }); });});
"Wygeneruj testy dla niezmienników systemu:- Zachowanie wartości w transakcjach- Brak ujemnych holdingów po rebalansowaniu- Metryki ryzyka w prawidłowych zakresach- Obliczenia podatkowe nigdy ujemneStwórz testy właściwości dla każdego niezmiennika"
"Stwórz testy fuzz dla:- Granic walidacji wejścia- Limitów precyzji obliczeń- Operacji równoczesnych- Spójności stanuGeneruj losowe ale prawidłowe dane testowe"
{ "mcpServers": { "playwright": { "command": "npx", "args": ["-y", "@modelcontextprotocol/server-puppeteer"] } }}
"Używając Playwright MCP, przetestuj dashboard inwestycyjny:1. Przejdź do localhost:30002. Zaloguj się danymi testowymi3. Weryfikuj że wartość portfela wyświetla się poprawnie4. Kliknij przycisk 'Rebalance'5. Zrób screenshot modalu potwierdzenia6. Weryfikuj że pojawia się komunikat sukcesu"
// MCP obsługuje automatyzację przeglądarki"Używając Playwright MCP:- Wypełnij formularz odsetek składanych z kapitał=10000, stopa=5%, czas=10- Kliknij przycisk Calculate- Weryfikuj że wynik pokazuje $16,288.95- Zrób screenshot dla testu regresji wizualnej"
"Używając Playwright MCP:- Przejdź do strony biblioteki komponentów- Zrób screenshot każdego komponentu kalkulatora- Porównaj z obrazami baseline- Raportuj wszelkie różnice wizualne"
"Testuj responsywny design z Playwright MCP:- Ustaw viewport na mobile (375x667)- Zrób screenshot dashboard- Ustaw viewport na tablet (768x1024)- Zrób screenshot dashboard- Ustaw viewport na desktop (1920x1080)- Zrób screenshot dashboard- Weryfikuj że wszystkie layouty renderują się poprawnie"
"Używając Playwright MCP, testuj w różnych przeglądarkach:- Uruchom zestaw testów w Chromium- Uruchom zestaw testów w Firefox- Uruchom zestaw testów w WebKit- Porównaj wyniki i timing"
"Używając Playwright MCP, uruchom audyt dostępności:- Przejdź do każdej strony- Uruchom skan dostępności axe- Sprawdź współczynniki kontrastu kolorów- Weryfikuj nawigację klawiaturą- Testuj ogłoszenia screen readera- Wygeneruj raport dostępności"
"Używając Playwright MCP, zmierz wydajność:- Przejdź do dashboard z śledzeniem wydajności- Zmierz time to interactive (TTI)- Sprawdź largest contentful paint (LCP)- Monitoruj cumulative layout shift (CLS)- Wygeneruj raport wydajności z metrykami"
// Symulacja real user monitoring"Symuluj podróż użytkownika z Playwright MCP:1. Wyląduj na homepage (zmierz czas ładowania)2. Przejdź do kalkulatora (zmierz przejście)3. Wykonaj 10 obliczeń (zmierz responsywność)4. Wygeneruj raport (zmierz czas renderowania)Zarejestruj wszystkie metryki wydajności"
Testowanie równoległe:
"Używając Playwright MCP, uruchom testy równolegle:- Test 1: Przepływ rebalansowania portfela- Test 2: Workflow obliczeń podatkowych- Test 3: Generowanie raportów- Test 4: Aktualizacja ustawień użytkownikaUruchom wszystkie jednocześnie i raportuj wyniki"
Mockowanie API:
"Używając Playwright MCP z przechwytywaniem sieci:- Mockuj odpowiedzi API danych rynkowych- Testuj scenariusze błędów (500, timeout)- Weryfikuj UI obsługi błędów- Testuj stany ładowania- Waliduj mechanizmy retry"
Testowanie zarządzania stanem:
"Testuj złożony stan z Playwright MCP:- Otwórz aplikację w dwóch kartach przeglądarki- Aktualizuj portfel w karcie 1- Weryfikuj że karta 2 odzwierciedla zmiany- Testuj aktualizacje optymistyczne- Weryfikuj rozwiązywanie konfliktów"
"Stwórz testy integracji dla kompletnych workflow:- Wdrażanie nowego inwestora z początkowym portfelem- Miesięczne rebalansowanie z aktualizacjami rynku- Tax loss harvesting na koniec roku- Generowanie raportów wydajnościTestuj pełny przepływ z realistycznymi danymi"
Przykład testu integracji:
describe('Integracja workflow inwestora', () => { let investor: Investor; let marketData: MarketDataService;
beforeEach(() => { investor = createInvestor({ initialDeposit: 100000, riskTolerance: 'moderate', taxBracket: 0.32, }); marketData = new MockMarketDataService(); });
it('powinno obsłużyć kompletny miesięczny workflow rebalansowania', async () => { // Początkowe tworzenie portfela const portfolio = await createPortfolio(investor, { strategy: 'aggressive-growth', constraints: { minCash: 5000, maxSinglePosition: 0.25, }, });
expect(portfolio.holdings).toHaveLength(15); expectPortfolioValid(portfolio);
// Symuluj miesiąc ruchów rynkowych await marketData.simulateMonth();
// Sprawdź potrzebę rebalansowania const analysis = analyzePortfolio(portfolio, marketData); expect(analysis.rebalancingNeeded).toBe(true); expect(analysis.drift).toBeGreaterThan(0.05);
// Wykonaj rebalansowanie const trades = await rebalancePortfolio(portfolio, { marketData, taxAware: true, minTradeSize: 100, });
expect(trades).toHaveLength(8); expect(trades.every(t => t.taxImpact !== undefined)).toBe(true);
// Zastosuj transakcje i weryfikuj const newPortfolio = applyTrades(portfolio, trades); expectPortfolioValid(newPortfolio); expect(calculateDrift(newPortfolio)).toBeLessThan(0.01); });});
"Stwórz benchmarki wydajności:- Oblicz 10,000 portfeli w poniżej 1 sekundy- Rebalansuj portfel 1,000 aktywów w poniżej 100ms- Wygeneruj raport podatkowy za 1 rok w poniżej 500ms- Obliczenia ryzyka na 5 lat danych w poniżej 50msDołącz testy użycia pamięci"
"Zaprojektuj testy obciążenia dla:- Równoczesnych obliczeń portfela- Aktualizacji danych rynkowych podczas rebalansowania- Wielu żądań optymalizacji podatkowej- Generowania raportów pod obciążeniemZapewnij thread safety i dokładność"
"Stwórz kompleksową dokumentację testów:- Raport pokrycia testami z wyjaśnieniami- Katalog przypadków brzegowych z przykładami- Baseline benchmarków wydajności- Przewodnik generowania danych testowych- Przewodnik rozwiązywania problemów z błędami"
"Zbuduj wizualne raportowanie testów:- Mapy cieplne pokrycia- Wykresy trendów wydajności- Wykrywanie flaky testów- Timeline wykonania testów- Analiza wzorców błędów"
"Stwórz zaawansowane helpery testowe:- Testowanie snapshot dla obliczeń- Testowanie golden file dla raportów- Konfiguracja mutation testing- Contract testing dla API- Automatyzacja testów regresji"
Używaj AI i MCP do znajdowania przypadków brzegowych:
// Metoda 1: Używanie Playwright MCP do testowania UI"Używając Playwright MCP, eksploruj UI kalkulatora i znajdź:- Przypadki brzegowe walidacji wejścia- Stany UI nie pokryte testami jednostkowymi- Problemy dostępności- Wąskie gardła wydajności w renderowaniu"
// Metoda 2: Używanie bazy danych MCP do testowania danych"Używając PostgreSQL MCP, przeanalizuj schemat i sugeruj:- Przypadki testowe naruszenia ograniczeń- Testy izolacji transakcji potrzebne- Scenariusze testów wydajności- Przypadki brzegowe integralności danych"
// Metoda 3: Ręczna analiza implementacji@implementation/calculator.ts"Przeanalizuj tę implementację i sugeruj:1. Brakujące przypadki testowe2. Potencjalne przypadki brzegowe3. Warunki błędów nie obsłużone4. Wąskie gardła wydajności5. Problemy precyzji"
Podążaj za ścisłym cyklem TDD:
// 1. Napisz failing testit('powinno obliczyć oszczędności tax-loss harvesting', () => { const result = calculateTaxLossHarvesting({ losses: 5000, gains: 8000, taxRate: 0.32, }); expect(result.taxSavings).toBe(960); // (5000 - 3000) * 0.32});
// 2. Zobacz jak się nie udaje// ❌ calculateTaxLossHarvesting is not defined
// 3. Implementuj minimum do przejściafunction calculateTaxLossHarvesting({ losses, gains, taxRate }) { const netLoss = Math.min(losses, gains); return { taxSavings: netLoss * taxRate };}
// 4. Refaktoryzuj z pewnością
Zapewnij jakość testów:
// Skonfiguruj Stryker dla mutation testing{ "mutator": { "name": "typescript", "excludedMutations": ["StringLiteral"] }, "testRunner": "jest", "coverageAnalysis": "perTest", "thresholds": { "high": 90, "low": 85, "break": 80 }}
Problem: Testy psują się przy drobnym refaktorowaniu
Rozwiązanie:
// Źle: Testowanie szczegółów implementacjiit('powinno wywołać Math.pow', () => { const spy = jest.spyOn(Math, 'pow'); calculateCompoundInterest({ ... }); expect(spy).toHaveBeenCalled();});
// Dobrze: Testowanie zachowaniait('powinno obliczyć poprawny wynik', () => { const result = calculateCompoundInterest({ ... }); expectFinanciallyEqual(result.finalAmount, 1628.89);});
Problem: Brakujące przypadki brzegowe i ścieżki błędów
Rozwiązanie:
// Używaj AI do odkrywania przypadków brzegowych"Jakie przypadki brzegowe powinienem testować dla funkcji która:- Dzieli dwie liczby- Obsługuje waluty- Pracuje z wejściem użytkownika"
// AI sugeruje: dzielenie przez zero, precyzja,// przepełnienie, NaN, wartości ujemne, itd.
Problem: Zestaw testów trwa zbyt długo
Rozwiązanie:
// Zrównoleglij niezależne testydescribe.concurrent('Calculator Tests', () => { it.concurrent('test 1', async () => { ... }); it.concurrent('test 2', async () => { ... });});
// Mockuj kosztowne operacjejest.mock('./market-data-api');
Zaawansowane techniki testowania:
Contract testing
Chaos testing
Testowanie wizualne
Testowanie napędzane MCP
Twoja implementacja TDD jest udana, gdy:
Zespoły używające TDD wspomaganego AI raportują:
Przed pisaniem implementacji:
Opanowałeś TDD z AI. Gotowy na więcej?
Mutation testing
Skonfiguruj zaawansowane mutation testing
Automatyzacja testów
Zbuduj generowanie testów napędzane AI
Testowanie wydajności
Stwórz kompleksowy zestaw wydajności
Kontynuuj do Optymalizacja wydajności →