- Testing Strategies
- When to use this skill
- New project
-
- define a testing strategy
- Quality issues
-
- bugs happen frequently
- Before refactoring
-
- build a safety net
- CI/CD setup
- automated tests
Instructions
Step 1: Understand the Test Pyramid
/\
/E2E\ ← few (slow, expensive)
/__\
/ \
/Integration\ ← medium
/_\
/ \
/ Unit Tests \ ← many (fast, inexpensive)
/_______\
Ratio guide
:
Unit: 70%
Integration: 20%
E2E: 10%
Step 2: Unit testing strategy
Given-When-Then pattern
:
describe
(
'calculateDiscount'
,
(
)
=>
{
it
(
'should apply 10% discount for orders over $100'
,
(
)
=>
{
// Given: setup
const
order
=
{
total
:
150
,
customerId
:
'123'
}
;
// When: perform action
const
discount
=
calculateDiscount
(
order
)
;
// Then: verify result
expect
(
discount
)
.
toBe
(
15
)
;
}
)
;
it
(
'should not apply discount for orders under $100'
,
(
)
=>
{
const
order
=
{
total
:
50
,
customerId
:
'123'
}
;
const
discount
=
calculateDiscount
(
order
)
;
expect
(
discount
)
.
toBe
(
0
)
;
}
)
;
it
(
'should throw error for invalid order'
,
(
)
=>
{
const
order
=
{
total
:
-
10
,
customerId
:
'123'
}
;
expect
(
(
)
=>
calculateDiscount
(
order
)
)
.
toThrow
(
'Invalid order'
)
;
}
)
;
}
)
;
Mocking strategy
:
// Mock external dependencies
jest
.
mock
(
'../services/emailService'
)
;
import
{
sendEmail
}
from
'../services/emailService'
;
describe
(
'UserService'
,
(
)
=>
{
it
(
'should send welcome email on registration'
,
async
(
)
=>
{
// Arrange
const
mockSendEmail
=
sendEmail
as
jest
.
MockedFunction
<
typeof
sendEmail
; mockSendEmail . mockResolvedValueOnce ( true ) ; // Act await userService . register ( { email : 'test@example.com' , password : 'pass' } ) ; // Assert expect ( mockSendEmail ) . toHaveBeenCalledWith ( { to : 'test@example.com' , subject : 'Welcome!' , body : expect . any ( String ) } ) ; } ) ; } ) ; Step 3: Integration Testing API endpoint tests : describe ( 'POST /api/users' , ( ) => { beforeEach ( async ( ) => { await db . user . deleteMany ( ) ; // Clean DB } ) ; it ( 'should create user with valid data' , async ( ) => { const response = await request ( app ) . post ( '/api/users' ) . send ( { email : 'test@example.com' , username : 'testuser' , password : 'Password123!' } ) ; expect ( response . status ) . toBe ( 201 ) ; expect ( response . body . user ) . toMatchObject ( { email : 'test@example.com' , username : 'testuser' } ) ; // Verify it was actually saved to the DB const user = await db . user . findUnique ( { where : { email : 'test@example.com' } } ) ; expect ( user ) . toBeTruthy ( ) ; } ) ; it ( 'should reject duplicate email' , async ( ) => { // Create first user await request ( app ) . post ( '/api/users' ) . send ( { email : 'test@example.com' , username : 'user1' , password : 'Pass123!' } ) ; // Attempt duplicate const response = await request ( app ) . post ( '/api/users' ) . send ( { email : 'test@example.com' , username : 'user2' , password : 'Pass123!' } ) ; expect ( response . status ) . toBe ( 409 ) ; } ) ; } ) ; Step 4: E2E Testing (Playwright) import { test , expect } from '@playwright/test' ; test . describe ( 'User Registration Flow' , ( ) => { test ( 'should complete full registration process' , async ( { page } ) => { // 1. Visit homepage await page . goto ( 'http://localhost:3000' ) ; // 2. Click Sign Up button await page . click ( 'text=Sign Up' ) ; // 3. Fill out form await page . fill ( 'input[name="email"]' , 'test@example.com' ) ; await page . fill ( 'input[name="username"]' , 'testuser' ) ; await page . fill ( 'input[name="password"]' , 'Password123!' ) ; // 4. Submit await page . click ( 'button[type="submit"]' ) ; // 5. Confirm success message await expect ( page . locator ( 'text=Welcome' ) ) . toBeVisible ( ) ; // 6. Confirm redirect to dashboard await expect ( page ) . toHaveURL ( 'http://localhost:3000/dashboard' ) ; // 7. Confirm user info is displayed await expect ( page . locator ( 'text=testuser' ) ) . toBeVisible ( ) ; } ) ; test ( 'should show error for invalid email' , async ( { page } ) => { await page . goto ( 'http://localhost:3000/signup' ) ; await page . fill ( 'input[name="email"]' , 'invalid-email' ) ; await page . fill ( 'input[name="password"]' , 'Password123!' ) ; await page . click ( 'button[type="submit"]' ) ; await expect ( page . locator ( 'text=Invalid email' ) ) . toBeVisible ( ) ; } ) ; } ) ; Step 5: TDD (Test-Driven Development) Red-Green-Refactor Cycle : // 1. RED: write a failing test describe ( 'isPalindrome' , ( ) => { it ( 'should return true for palindrome' , ( ) => { expect ( isPalindrome ( 'racecar' ) ) . toBe ( true ) ; } ) ; } ) ; // 2. GREEN: minimal code to pass the test function isPalindrome ( str : string ) : boolean { return str === str . split ( '' ) . reverse ( ) . join ( '' ) ; } // 3. REFACTOR: improve the code function isPalindrome ( str : string ) : boolean { const cleaned = str . toLowerCase ( ) . replace ( / [ ^ a - z 0 - 9 ] / g , '' ) ; return cleaned === cleaned . split ( '' ) . reverse ( ) . join ( '' ) ; } // 4. Additional test cases it ( 'should ignore case and spaces' , ( ) => { expect ( isPalindrome ( 'A man a plan a canal Panama' ) ) . toBe ( true ) ; } ) ; it ( 'should return false for non-palindrome' , ( ) => { expect ( isPalindrome ( 'hello' ) ) . toBe ( false ) ; } ) ; Output format Testing strategy document
Testing Strategy
Coverage Goals
Unit Tests: 80%
Integration Tests: 60%
E2E Tests: Critical user flows
Test Execution
Unit: Every commit (local + CI)
Integration: Every PR
E2E: Before deployment
Tools
Unit: Jest
Integration: Supertest
E2E: Playwright
Coverage: Istanbul/nyc
CI/CD Integration
GitHub Actions: Run all tests on PR
Fail build if coverage < 80%
- E2E tests on staging environment
- Constraints
- Required rules (MUST)
- Test isolation
-
- each test is independent
- Fast feedback
-
- unit tests should be fast (<1 min)
- Deterministic
-
- same input → same result
- Prohibited items (MUST NOT)
- Test dependencies
-
- do not let test A depend on test B
- Production DB
-
- do not use a real DB in tests
- Sleep/Timeout
-
- avoid time-based tests
- Best practices
- AAA pattern
-
- Arrange-Act-Assert
- Test names
-
- "should ... when ..."
- Edge Cases
-
- boundary values, null, empty values
- Happy Path + Sad Path
-
- both success/failure scenarios
- References
- Test Pyramid
- Jest
- Playwright
- Testing Best Practices
- Metadata
- Version
- Current version
-
- 1.0.0
- Last updated
-
- 2025-01-01
- Compatible platforms
- Claude, ChatGPT, Gemini