Pulled ALL skills from 15 source repositories: - anthropics/skills: 16 (docs, design, MCP, testing) - obra/superpowers: 14 (TDD, debugging, agents, planning) - coreyhaines31/marketingskills: 25 (marketing, CRO, SEO, growth) - better-auth/skills: 5 (auth patterns) - vercel-labs/agent-skills: 5 (React, design, Vercel) - antfu/skills: 16 (Vue, Vite, Vitest, pnpm, Turborepo) - Plus 13 individual skills from various repos Mosaic Stack is not limited to coding — the Orchestrator and subagents serve coding, business, design, marketing, writing, logistics, analysis, and more. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
6.1 KiB
6.1 KiB
title, impact, impactDescription, type, tags
| title | impact | impactDescription | type | tags | ||||||
|---|---|---|---|---|---|---|---|---|---|---|
| Avoid Snapshot-Only Tests - They Don't Prove Correctness | MEDIUM | Snapshot tests verify structure but not functionality, leading to false confidence and brittle tests | best-practice |
|
Avoid Snapshot-Only Tests - They Don't Prove Correctness
Impact: MEDIUM - Snapshot tests only verify that HTML structure hasn't changed - they don't verify that the component works correctly. Relying exclusively on snapshots leads to false confidence and tests that break on any refactoring, even when functionality is preserved.
Use snapshots sparingly for regression detection. Prefer behavioral assertions that test what the component does.
Task Checklist
- Don't use snapshots as the only assertion for component behavior
- Use snapshots for regression detection on stable UI components
- Always pair snapshots with behavioral assertions
- Keep snapshots small and focused (avoid full component snapshots)
- Review snapshot diffs carefully - don't blindly update
- Consider inline snapshots for small, critical structures
Incorrect:
import { mount } from '@vue/test-utils'
import UserCard from './UserCard.vue'
// BAD: Snapshot-only test proves nothing about functionality
test('UserCard renders correctly', () => {
const wrapper = mount(UserCard, {
props: { user: { name: 'John', email: 'john@example.com' } }
})
expect(wrapper.html()).toMatchSnapshot()
})
// This test passes even if:
// - The email isn't clickable
// - The avatar doesn't load
// - User actions are completely broken
// - Accessibility is broken
Correct:
import { mount } from '@vue/test-utils'
import UserCard from './UserCard.vue'
// CORRECT: Test actual behavior
test('UserCard displays user information', () => {
const wrapper = mount(UserCard, {
props: { user: { name: 'John', email: 'john@example.com' } }
})
expect(wrapper.find('[data-testid="user-name"]').text()).toBe('John')
expect(wrapper.find('[data-testid="user-email"]').text()).toBe('john@example.com')
})
test('UserCard email link is clickable', async () => {
const wrapper = mount(UserCard, {
props: { user: { name: 'John', email: 'john@example.com' } }
})
const emailLink = wrapper.find('a[href^="mailto:"]')
expect(emailLink.exists()).toBe(true)
expect(emailLink.attributes('href')).toBe('mailto:john@example.com')
})
test('UserCard emits select event when clicked', async () => {
const wrapper = mount(UserCard, {
props: { user: { id: 1, name: 'John' } }
})
await wrapper.trigger('click')
expect(wrapper.emitted('select')).toBeTruthy()
expect(wrapper.emitted('select')[0]).toEqual([{ id: 1, name: 'John' }])
})
When Snapshots ARE Useful
Regression Detection for Stable Components
// ACCEPTABLE: Snapshot as additional check, not the only check
test('ErrorBoundary renders error message', () => {
const wrapper = mount(ErrorBoundary, {
props: { error: new Error('Something went wrong') }
})
// Primary assertions - verify behavior
expect(wrapper.find('.error-title').text()).toBe('Error')
expect(wrapper.find('.error-message').text()).toContain('Something went wrong')
// Secondary snapshot - catches unexpected structural changes
expect(wrapper.find('.error-container').html()).toMatchSnapshot()
})
Inline Snapshots for Small Structures
// ACCEPTABLE: Inline snapshot for small, critical structure
test('generates correct list markup', () => {
const wrapper = mount(ListItem, { props: { item: 'Test' } })
expect(wrapper.html()).toMatchInlineSnapshot(`
"<li class="list-item">Test</li>"
`)
})
Complex SVG or Icon Output
// ACCEPTABLE: Snapshot for complex generated content
test('renders correct chart SVG', () => {
const wrapper = mount(PieChart, {
props: { data: [30, 40, 30] }
})
// Verify key behavior
expect(wrapper.findAll('path').length).toBe(3)
// Snapshot for full SVG structure
expect(wrapper.find('svg').html()).toMatchSnapshot()
})
Better Alternatives to Snapshots
Test Specific Elements
// Instead of snapshotting entire component
test('renders product with all required fields', () => {
const wrapper = mount(ProductCard, {
props: { product: { name: 'Widget', price: 9.99, inStock: true } }
})
expect(wrapper.find('.product-name').text()).toBe('Widget')
expect(wrapper.find('.product-price').text()).toContain('9.99')
expect(wrapper.find('.in-stock-badge').exists()).toBe(true)
})
Test CSS Classes for Styling
test('applies danger styling for errors', () => {
const wrapper = mount(Alert, {
props: { type: 'error', message: 'Failed!' }
})
expect(wrapper.classes()).toContain('alert-danger')
expect(wrapper.find('.alert-icon').classes()).toContain('icon-error')
})
Use Testing Library Queries
import { render, screen } from '@testing-library/vue'
test('form has accessible labels', () => {
render(LoginForm)
// Testing Library queries verify accessibility
expect(screen.getByLabelText('Email')).toBeInTheDocument()
expect(screen.getByLabelText('Password')).toBeInTheDocument()
expect(screen.getByRole('button', { name: 'Sign In' })).toBeInTheDocument()
})
Snapshot Anti-Patterns
// ANTI-PATTERN: Giant component snapshot
test('page renders', () => {
const wrapper = mount(EntirePageComponent)
expect(wrapper.html()).toMatchSnapshot() // 500+ lines of HTML
})
// ANTI-PATTERN: Snapshot with dynamic content
test('shows current date', () => {
const wrapper = mount(DateDisplay)
expect(wrapper.html()).toMatchSnapshot() // Fails every day!
})
// ANTI-PATTERN: Snapshot after every test
test('button works', async () => {
const wrapper = mount(Counter)
await wrapper.find('button').trigger('click')
expect(wrapper.html()).toMatchSnapshot() // Redundant
})