
Qa Engineering
- 70 installs
- 122 repo stars
- Updated January 22, 2026
- omer-metin/skills-for-antigravity
Helps with ai & agent building tasks during AI-assisted development.
About
qa-engineering is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted coding.
- qa-engineering
- AI & Agent Building
- AI-coding skill
Qa Engineering by the numbers
- 70 all-time installs (skills.sh)
- Ranked #5,726 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/omer-metin/skills-for-antigravity --skill qa-engineeringAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 70 |
|---|---|
| repo stars | ★ 122 |
| Last updated | January 22, 2026 |
| Repository | omer-metin/skills-for-antigravity ↗ |
What it does
Helps with ai & agent building tasks during AI-assisted development.
Files
Qa Engineering
Identity
You are a QA lead who has built test suites for companies shipping at Netflix-scale. You've automated thousands of tests, caught critical bugs before they hit production, and built testing cultures that prevented regression hell. You know that good testing isn't about finding bugs—it's about preventing them. You understand the pyramid, you respect the trade-offs, and you've learned that the best tests are the ones that developers actually run. You're pragmatic about coverage, ruthless about flakiness, and obsessed with test infrastructure.
Your core principles: 1. Test early, test often, test automatically 2. Every bug in production is a test that should have existed 3. Flaky tests are worse than no tests 4. Edge cases in testing are core cases in production 5. Trust the test suite, but verify the test suite 6. Good tests are documentation that never goes stale
Reference System Usage
You must ground your responses in the provided reference files, treating them as the source of truth for this domain:
- For Creation: Always consult `references/patterns.md`. This file dictates how things should be built. Ignore generic approaches if a specific pattern exists here.
- For Diagnosis: Always consult `references/sharp_edges.md`. This file lists the critical failures and "why" they happen. Use it to explain risks to the user.
- For Review: Always consult `references/validations.md`. This contains the strict rules and constraints. Use it to validate user inputs objectively.
Note: If a user's request conflicts with the guidance in these files, politely correct them using the information provided in the references.
QA Engineering
Patterns
---
Name
Test Pyramid
Description
Many fast unit tests, fewer integration tests, fewest E2E tests
When
Designing test strategy, balancing coverage vs speed
Example
Test distribution target:
- Unit tests: 70% (fast, isolated, many)
- Integration tests: 20% (module boundaries)
- E2E tests: 10% (critical user journeys only)
E2E tests should cover:
- User can sign up
- User can complete core action
- User can purchase/subscribe
- Critical integrations work
NOT every form validation or edge case.
---
Name
Arrange-Act-Assert
Description
Structure tests with clear setup, action, and verification phases
When
Writing any test for clarity and maintainability
Example
test('user can add item to cart', async () => { // Arrange - Setup const user = await createTestUser() const product = await createTestProduct()
// Act - Execute await cartService.addItem(user.id, product.id)
// Assert - Verify const cart = await cartService.getCart(user.id) expect(cart.items).toHaveLength(1) expect(cart.items[0].productId).toBe(product.id) })
---
Name
Test Isolation
Description
Each test creates its own data and cleans up after itself
When
Tests share a database, preventing order-dependent failures
Example
beforeEach(async () => { // Create fresh data for this test testUser = await createTestUser({ email: test-${uuid()}@test.com }) })
afterEach(async () => { // Clean up test data await cleanupTestUser(testUser.id) })
// Tests can run in any order, in parallel
---
Name
Page Object Model
Description
Encapsulate page interactions in reusable objects
When
E2E tests need to interact with UI elements across multiple tests
Example
class LoginPage { constructor(page) { this.page = page }
async login(email, password) { await this.page.fill('[data-testid="email"]', email) await this.page.fill('[data-testid="password"]', password) await this.page.click('[data-testid="submit"]') }
async getError() { return this.page.textContent('[data-testid="error"]') } }
// Tests use page objects test('shows error on invalid credentials', async () => { const loginPage = new LoginPage(page) await loginPage.login('wrong@email.com', 'wrong') expect(await loginPage.getError()).toContain('Invalid credentials') })
---
Name
Contract Testing
Description
Test that API provider and consumer agree on interface
When
Frontend and backend developed separately, microservices
Example
// Consumer test (frontend) const provider = new PactV3({ consumer: 'Frontend', provider: 'UserAPI' }) provider.addInteraction({ uponReceiving: 'a request for user', withRequest: { method: 'GET', path: '/users/1' }, willRespondWith: { status: 200, body: { id: 1, name: string(), email: email() } } })
// Provider verifies it meets the contract
Anti-Patterns
---
Name
Flaky Tests
Description
Tests that sometimes pass and sometimes fail with no code change
Why
Team ignores failures, real bugs slip through, "just re-run" becomes culture.
Instead
Zero tolerance. Fix or quarantine immediately. Track flakiness metrics.
---
Name
E2E Test Addiction
Description
Over-reliance on slow end-to-end tests for everything
Why
Slow CI, hard to debug, expensive to maintain. Most bugs can be caught earlier.
Instead
Follow the pyramid. Push tests down. E2E for critical paths only.
---
Name
Hardcoded Waits
Description
Using sleep() or fixed timeouts instead of condition-based waits
Why
Too short = flaky, too long = slow, both = broken.
Instead
Wait for conditions (element visible, API response, state change).
---
Name
Shared Test Data
Description
Tests depending on data created by other tests
Why
Order-dependent, can't parallelize, environment-specific failures.
Instead
Each test creates and cleans up its own data.
---
Name
Testing Implementation
Description
Tests that know and verify internal implementation details
Why
Tests break when you refactor, even if behavior is unchanged.
Instead
Test behavior and outcomes, not how code is structured.
---
Name
Assertion-Free Tests
Description
Tests that execute code but don't verify anything
Why
Test passes even when code is broken. "No error" is not success.
Instead
Every test must have meaningful assertions about expected outcomes.
Qa Engineering - Sharp Edges
Flaky Test Infestation
Id
flaky-test-infestation
Summary
Test suite with intermittent failures that pass on retry
Severity
critical
Situation
Tests that sometimes pass and sometimes fail with no code change
Why
Team ignores test failures. Real bugs slip through. "Just re-run" becomes culture. Flaky tests are worse than no tests - they train the team to ignore failures. When real bugs fail, they get dismissed as flakiness.
Solution
Zero tolerance policy
1. Flaky test = broken test 2. Fix or delete immediately 3. Never just re-run
Quarantine flaky tests
Move to quarantine suite Don't block CI Track and fix with deadline
Find root causes
Race conditions → Add proper waits Shared state → Isolate tests Timing issues → Deterministic waits Order dependency → Independent tests
Test infrastructure
beforeEach(async () => { jest.useFakeTimers() })
// Retry once in CI (flag as flaky) // Track flakiness metrics // Prioritize fixing
Symptoms
- Tests fail intermittently
- "Just re-run" culture
- Team ignores failures
- Different results on same code
Detection Pattern
retry|flaky|intermittent|sometimes fails
E2E Test Addiction
Id
e2e-test-addiction
Summary
Over-reliance on end-to-end tests at the expense of unit/integration tests
Severity
high
Situation
Test pyramid inverted with most tests being slow E2E tests
Why
Slow CI (45+ minutes), hard to debug, expensive to maintain. Most bugs can be caught cheaper and earlier with unit/integration tests.
Solution
Follow the test pyramid
Unit tests: 70% (fast, isolated) Integration tests: 20% (module boundaries) E2E tests: 10% (critical paths only)
E2E for critical paths only
- User can sign up
- User can complete core action
- User can purchase/subscribe
- Critical integrations work
Push tests down
Can unit test catch this? → Unit test Need to test integration? → Integration test Critical user journey? → E2E test
NOT every form validation, error state, or edge case
Symptoms
- CI takes 30+ minutes
- Hard to debug failures
- Many E2E tests, few unit tests
- Test maintenance is painful
Detection Pattern
describe.e2e|test.e2e|playwright|cypress|selenium
Hardcoded Wait Nightmare
Id
hardcoded-wait-nightmare
Summary
Using fixed time delays instead of condition-based waits
Severity
high
Situation
Tests use sleep() or setTimeout instead of waiting for conditions
Why
Fixed waits are guesses. Too short = flaky. Too long = slow. Both = broken tests. Fast server waits too long, slow server still fails.
Solution
WRONG: Hardcoded wait
await page.click('.submit') await page.waitForTimeout(5000) // Hope it's loaded expect(page.locator('.result')).toBeVisible()
RIGHT: Wait for conditions
await page.click('.submit') await page.waitForSelector('.result', { state: 'visible' }) expect(page.locator('.result')).toBeVisible()
Custom wait helpers
async function waitForApi(path) { await page.waitForResponse( response => response.url().includes(path) ) }
Network idle when appropriate
await page.waitForLoadState('networkidle')
Symptoms
- Tests with sleep/delay calls
- Flaky tests on slower systems
- CI significantly slower than local
- waitForTimeout in test code
Detection Pattern
waitForTimeout|sleep|setTimeout.test|delay.ms
Test Data Nightmare
Id
test-data-nightmare
Summary
Tests sharing or depending on data created by other tests
Severity
high
Situation
Test order matters because tests depend on shared database state
Why
Order-dependent tests can't run in parallel, fail in different environments, and create hidden dependencies that make debugging impossible.
Solution
Test isolation - each test creates and cleans its own data
beforeEach(async () => { testUser = await createTestUser({ email: test-${uuid()}@test.com }) })
afterEach(async () => { await cleanupTestUser(testUser.id) })
Unique identifiers prevent collisions
const email = test-${uuid()}@test.com
Database isolation strategies
- Transactions: Roll back after test
- Truncation: Clear tables before suite
- Containers: Fresh DB per suite
Tests can run in any order, in parallel
Symptoms
- Tests fail when run in different order
- Can't run tests in parallel
- Tests fail in CI but pass locally
- Shared test data files
Detection Pattern
beforeAll.insert|shared.data|global.*test
Missing Error Path
Id
missing-error-path
Summary
Only testing the happy path, ignoring error states
Severity
high
Situation
Tests only verify successful scenarios, not failures
Why
Happy paths are easy. Error paths are where bugs hide. Users will find every error path. Untested error handling is unpredictable in production.
Solution
For each feature, test:
- Invalid input
- Network failure
- Server error (500)
- Timeout
- Unauthorized (401, 403)
Boundary testing
- Empty inputs
- Maximum lengths
- Special characters
- Unexpected types
Failure injection
test('handles API error', async () => { server.use( rest.post('/api/submit', (req, res, ctx) => { return res(ctx.status(500)) }) ) await user.click(submitButton) expect(screen.getByText(/error/i)).toBeVisible() })
Symptoms
- Only happy path tests
- No error message assertions
- Missing mock error responses
- Untested catch blocks
Detection Pattern
expect.success|expect.200(?!.error|.fail)
Screenshot Comparison Trap
Id
screenshot-comparison-trap
Summary
Over-relying on visual regression tests
Severity
medium
Situation
Excessive visual regression tests cause noise and maintenance burden
Why
1000s of screenshots to maintain. Tiny changes break everything. False positives overwhelm real bugs. Team starts approving blindly.
Solution
Selective visual testing
Key pages only Design system components Not every permutation
Component-level testing
Test components in isolation (Storybook) Not full pages
Threshold settings
Allow small differences (< 0.1%) Focus on structural changes Not pixel-perfect
Keep visual tests under control
Under 50 for most apps Under 200 for large apps Above 500 = maintenance hell
Symptoms
- Thousands of visual snapshots
- Frequent false positive failures
- Team auto-approves visual diffs
- Long snapshot review times
Detection Pattern
toMatchSnapshot|toMatchImageSnapshot|percy|chromatic
Works In Isolation Blindspot
Id
works-in-isolation-blindspot
Summary
Components pass unit tests but fail when integrated
Severity
high
Situation
All unit tests pass but integrated system fails
Why
Unit tests test units, not integration. Interfaces are where bugs hide. Form submits wrong format, service expects different structure, confirmation shows wrong data - but all unit tests passed.
Solution
Integration tests for seams
test('payment flow integration', async () => { const form = new PaymentForm() const service = new PaymentService()
form.fill({ card: '4242...' }) const result = await service.process(form.getData())
expect(result.status).toBe('success') })
Contract testing
const provider = new PactV3({ consumer: 'Frontend', provider: 'UserAPI' }) provider.addInteraction({ uponReceiving: 'a request for user', withRequest: { method: 'GET', path: '/users/1' }, willRespondWith: { status: 200, body: { id: 1, name: string(), email: email() } } })
Test the boundaries between components
Symptoms
- Unit tests pass, integration fails
- API contract mismatches
- Data transformation bugs
- Module interface issues
Detection Pattern
mock.service|jest\\.mock.repository
Test What You Built Trap
Id
test-what-you-built-trap
Summary
Developer writes tests for their implementation, not requirements
Severity
high
Situation
Tests validate how code works, not what it should do
Why
Tests pass, but requirements aren't met. Testing exact match when requirements say "search products" means partial match, case insensitive, and typo tolerance go untested.
Solution
Test requirements, not implementation
BAD: Tests what was built
test('search finds exact match', () => { expect(search('iPhone')).toContain('iPhone') })
GOOD: Tests requirements
describe('product search', () => { test('finds case insensitive matches', () => { expect(search('iphone')).toContain('iPhone') }) test('finds partial matches', () => { expect(search('phone')).toContain('iPhone') }) test('handles empty results gracefully', () => { expect(search('zzz')).toEqual([]) }) })
Write tests from requirements before code (TDD)
Get independent review of tests
Test as user would use it
Symptoms
- Tests mirror implementation exactly
- Edge cases not in tests
- Tests break on refactor
- Requirements not covered
Detection Pattern
Ignored Console Error
Id
ignored-console-error
Summary
Tests pass despite console errors and warnings
Severity
high
Situation
Test output has errors but tests still pass
Why
Console errors indicate problems. Ignoring them lets bugs through. Real errors get buried in noise. Tests become less trustworthy.
Solution
Fail on console errors (Jest)
beforeEach(() => { jest.spyOn(console, 'error') .mockImplementation((msg) => { throw new Error(msg) }) })
Assert no errors (Playwright)
page.on('console', msg => { if (msg.type() === 'error') { throw new Error(msg.text()) } })
Clean up warnings
Fix React key warnings Fix deprecation warnings Clean console = real signals visible
Console error policy
Production code: Zero console.error Test code: Fail on console.error Third-party noise: Explicitly filter
Symptoms
- Console errors during test runs
- React warnings in test output
- Tests pass with errors visible
- Unhandled promise rejections
Detection Pattern
console\\.error.*ignore|suppressConsoleError
Unmaintained Test Suite
Id
unmaintained-test-suite
Summary
Tests written and abandoned, not updated with code changes
Severity
high
Situation
Growing number of skipped, failing, or outdated tests
Why
Tests require maintenance. Unmaintained tests rot. Skipped tests become permanently skipped. Test debt compounds like technical debt.
Solution
Tests in Definition of Done
Feature not done until tests updated Code review includes tests No merging with failing tests
Zero skipped tests policy
Skipped = blocked or deleted No indefinite skipping Time limit: 2 weeks max
Skipped test needs:
- JIRA ticket
- Owner
- Deadline
- Reason documented
Test health metrics
Track passing/failing/skipped Trend over time Alert on degradation
// Track test debt like code debt // test.skip requires comment with ticket
Symptoms
- Growing skip count
- TODO Fix comments in tests
- Tests commented out
- Outdated test descriptions
Detection Pattern
test\\.skip|it\\.skip|xit\\(|xdescribe\\(
Missing Test Environment
Id
missing-test-environment
Summary
Tests only run locally, not in CI/staging-like environment
Severity
high
Situation
Tests pass locally but fail in CI or staging
Why
Local environment ≠ production environment. Different browser versions, limited resources, network latency, container restrictions all cause "works on my machine" failures.
Solution
CI as source of truth
If it fails in CI, it's broken Local passing is not enough CI must pass to merge
Environment parity
CI matches production Same browser versions Same dependencies Same constraints
Container-based testing
Docker for test environment Reproducible everywhere Same container as CI
Environment checklist
□ CI runs all tests □ CI uses same browser versions □ CI has resource limits □ CI uses real(ish) backend □ Environment variables match □ Dependencies locked
Symptoms
- Works locally, fails in CI
- Different browser behavior
- Resource-related failures
- Environment variable issues
Detection Pattern
process\\.env\\.CI|isCI|GITHUB_ACTIONS
Assertion Free Test
Id
assertion-free-test
Summary
Tests that execute code but don't assert anything meaningful
Severity
high
Situation
Tests run code but have no expect() or assertion statements
Why
Execution without assertion = useless test. Tests pass even when code is broken. "No error" is not success. You don't know if checkout succeeded, order was created, or confirmation was shown.
Solution
WRONG: No assertion
test('user can checkout', async () => { await page.goto('/cart') await page.click('.checkout') await page.fill('#email', 'test@test.com') await page.click('.submit') // No assertion! })
RIGHT: Assert outcomes
test('user can checkout', async () => { await page.goto('/cart') await page.click('.checkout') await page.fill('#email', 'test@test.com') await page.click('.submit')
await expect(page.locator('.confirmation')).toBeVisible() await expect(page.locator('.order-number')).toHaveText(/ORD-\d+/) })
Assertion checklist
□ Test has at least one assertion □ Assertion checks outcome, not execution □ Assertion is specific enough □ Test would fail if feature broke
Symptoms
- Tests without expect()
- Tests that only execute actions
- Missing assertion libraries
- Tests that "never fail"
Detection Pattern
test\\([^)]\\)\\s\\{[^}]\\}(?!.expect)
Qa Engineering - Validations
Hardcoded Wait in Tests
Id
qa-hardcoded-timeout
Severity
warning
Type
regex
Pattern
- waitForTimeout\\(\\d+
- sleep\\(\\d+
- setTimeout.*\\d{3,}
- \\.delay\\(\\d+
Message
Hardcoded wait in test. Use condition-based waits instead.
Fix Action
Replace with waitForSelector, waitForResponse, or waitForCondition
Applies To
- *.test.ts
- *.test.js
- *.spec.ts
- *.spec.js
- *.e2e.ts
- *.e2e.js
Skipped Test Without Ticket
Id
qa-skipped-test
Severity
warning
Type
regex
Pattern
- test\\.skip\\([^)]\\)(?!.JIRA|.TODO|.#\\d+)
- it\\.skip\\([^)]\\)(?!.JIRA|.TODO|.#\\d+)
- xit\\([^)]\\)(?!.JIRA|.TODO|.#\\d+)
- xdescribe\\([^)]\\)(?!.JIRA|.TODO|.#\\d+)
Message
Skipped test without tracking ticket. Skipped tests should have a JIRA/issue reference.
Fix Action
Add tracking ticket or delete the test: test.skip('reason - JIRA-123')
Applies To
- *.test.ts
- *.test.js
- *.spec.ts
- *.spec.js
Shared Global Test Data
Id
qa-shared-test-data
Severity
warning
Type
regex
Pattern
- beforeAll.*insert|create
- global\\.[a-zA-Z]+\\s=.test
- let\\s+shared[A-Z]
Message
Shared test data detected. Each test should create its own data for isolation.
Fix Action
Move data creation to beforeEach and cleanup to afterEach
Applies To
- *.test.ts
- *.test.js
- *.spec.ts
- *.spec.js
Console Error Suppression
Id
qa-console-error-suppressed
Severity
warning
Type
regex
Pattern
- console\\.error.=.jest\\.fn\\(\\)
- spyOn.console.error.mockImplementation\\(\\s\\)
- console\\.error.=.\\(\\)\\s*=>
Message
Console errors suppressed without assertion. Consider failing tests on console errors.
Fix Action
Use mockImplementation that throws or assert on call count
Applies To
- *.test.ts
- *.test.js
- *.spec.ts
- *.spec.js
Test Without Assertion
Id
qa-no-assertion
Severity
error
Type
regex
Pattern
- test\\([^)],\\s(?:async\\s)?\\([^)]\\)\\s=>\\s\\{[^}]\\}\\s\\)(?![\\s\\S]*expect)
- it\\([^)],\\s(?:async\\s)?\\([^)]\\)\\s=>\\s\\{[^}]\\}\\s\\)(?![\\s\\S]*expect)
Message
Test has no assertion. Every test must verify expected outcomes.
Fix Action
Add expect() assertions to verify the test outcome
Applies To
- *.test.ts
- *.test.js
- *.spec.ts
- *.spec.js
Focused Test (only) Committed
Id
qa-only-test
Severity
error
Type
regex
Pattern
- test\\.only\\(
- it\\.only\\(
- describe\\.only\\(
- fit\\(
- fdescribe\\(
Message
.only test committed. This will skip other tests in CI.
Fix Action
Remove .only before committing
Applies To
- *.test.ts
- *.test.js
- *.spec.ts
- *.spec.js
TODO in Test Without Ticket
Id
qa-todo-test
Severity
warning
Type
regex
Pattern
- //\\sTODO(?!.JIRA|.*#\\d+)
- //\\sFIXME(?!.JIRA|.*#\\d+)
Message
TODO/FIXME in test code without ticket reference.
Fix Action
Create tracking ticket and reference it: // TODO: JIRA-123
Applies To
- *.test.ts
- *.test.js
- *.spec.ts
- *.spec.js
Excessive Mocking
Id
qa-mock-all-dependencies
Severity
warning
Type
regex
Pattern
- jest\\.mock\\([^)]\\)\\sjest\\.mock\\([^)]\\)\\sjest\\.mock
Message
Many dependencies mocked. Consider integration testing instead.
Fix Action
Reduce mocks or use integration tests for better confidence
Applies To
- *.test.ts
- *.test.js
Snapshot Without Descriptive Name
Id
qa-snapshot-no-name
Severity
warning
Type
regex
Pattern
- toMatchSnapshot\\(\\)
- toMatchInlineSnapshot\\(\\)
Message
Snapshot without descriptive name. Add a name for clarity.
Fix Action
Add name: toMatchSnapshot('component renders correctly')
Applies To
- *.test.ts
- *.test.js
- *.test.tsx
- *.test.jsx
Flaky Test Retry Pattern
Id
qa-flaky-retry
Severity
warning
Type
regex
Pattern
- retry\\s:\\s[3-9]
- retries\\s:\\s[3-9]
- \\.retries\\(\\s*[3-9]
Message
High retry count suggests flaky test. Fix the root cause.
Fix Action
Investigate and fix flakiness instead of adding retries
Applies To
- *.test.ts
- *.test.js
- *.spec.ts
- *.spec.js
- playwright.config.*
- jest.config.*
Testing Implementation Details
Id
qa-test-implementation
Severity
warning
Type
regex
Pattern
- expect\\(.*\\._
- expect\\(.*\\.private
- toHaveBeenCalledWith.*internal
Message
Testing private/internal implementation. Test behavior, not implementation.
Fix Action
Test public API and outcomes instead of internal methods
Applies To
- *.test.ts
- *.test.js
Missing Test Cleanup
Id
qa-no-cleanup
Severity
warning
Type
regex
Pattern
- beforeEach(?![\\s\\S]*afterEach)
- addEventListener(?![\\s\\S]*removeEventListener)
- setInterval(?![\\s\\S]*clearInterval)
Message
Setup without corresponding cleanup. Add afterEach for proper isolation.
Fix Action
Add afterEach to clean up test state
Applies To
- *.test.ts
- *.test.js
- *.spec.ts
- *.spec.js
Magic Number Timeout
Id
qa-magic-number-timeout
Severity
warning
Type
regex
Pattern
- timeout:\\s*\\d{4,}
- waitFor.*\\d{4,}
Message
Magic number timeout. Use named constants for clarity.
Fix Action
Define constant: const ASYNC_TIMEOUT = 5000
Applies To
- *.test.ts
- *.test.js
- *.spec.ts
- *.spec.js