feat: Complete fleet — 94 skills across 10+ domains
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>
This commit is contained in:
@@ -0,0 +1,144 @@
|
||||
---
|
||||
title: Test Components Using Blackbox Approach - Focus on Behavior Not Implementation
|
||||
impact: HIGH
|
||||
impactDescription: Implementation-aware tests become brittle and break during refactoring, leading to high maintenance burden
|
||||
type: best-practice
|
||||
tags: [vue3, testing, component-testing, vitest, vue-test-utils, blackbox]
|
||||
---
|
||||
|
||||
# Test Components Using Blackbox Approach - Focus on Behavior Not Implementation
|
||||
|
||||
**Impact: HIGH** - Tests that rely on implementation details (internal state, private methods, component structure) break during refactoring even when functionality remains correct. This leads to false negatives and high test maintenance burden.
|
||||
|
||||
Follow Kent C. Dodds' testing philosophy: "The more your tests resemble how your software is used, the more confidence they can give you."
|
||||
|
||||
## Task Checklist
|
||||
|
||||
- [ ] Test what the component does, not how it does it
|
||||
- [ ] Query elements by user-visible attributes (text, role, testid)
|
||||
- [ ] Simulate user interactions (click, type) rather than calling methods directly
|
||||
- [ ] Assert on rendered output, emitted events, and visible state changes
|
||||
- [ ] Avoid accessing component internal state or private methods
|
||||
- [ ] Use data-testid attributes for elements without semantic meaning
|
||||
|
||||
**Incorrect:**
|
||||
```javascript
|
||||
import { mount } from '@vue/test-utils'
|
||||
import Counter from './Counter.vue'
|
||||
|
||||
// BAD: Testing implementation details
|
||||
test('counter increments', async () => {
|
||||
const wrapper = mount(Counter)
|
||||
|
||||
// Accessing internal state directly
|
||||
expect(wrapper.vm.count).toBe(0)
|
||||
|
||||
// Calling internal method instead of simulating user action
|
||||
wrapper.vm.increment()
|
||||
|
||||
// Checking internal state instead of visible output
|
||||
expect(wrapper.vm.count).toBe(1)
|
||||
})
|
||||
|
||||
// BAD: Testing component structure
|
||||
test('has increment button', () => {
|
||||
const wrapper = mount(Counter)
|
||||
|
||||
// Testing implementation detail - what if button becomes an anchor?
|
||||
expect(wrapper.find('button').exists()).toBe(true)
|
||||
})
|
||||
```
|
||||
|
||||
**Correct:**
|
||||
```javascript
|
||||
import { mount } from '@vue/test-utils'
|
||||
import Counter from './Counter.vue'
|
||||
|
||||
// CORRECT: Testing behavior like a user would
|
||||
test('counter displays updated value after clicking increment', async () => {
|
||||
const wrapper = mount(Counter, {
|
||||
props: { max: 10 }
|
||||
})
|
||||
|
||||
// Assert initial visible state
|
||||
expect(wrapper.find('[data-testid="counter-value"]').text()).toContain('0')
|
||||
|
||||
// Simulate user action
|
||||
await wrapper.find('[data-testid="increment-button"]').trigger('click')
|
||||
|
||||
// Assert visible result
|
||||
expect(wrapper.find('[data-testid="counter-value"]').text()).toContain('1')
|
||||
})
|
||||
|
||||
// CORRECT: Testing emitted events (public API)
|
||||
test('emits change event with new value when incremented', async () => {
|
||||
const wrapper = mount(Counter)
|
||||
|
||||
await wrapper.find('[data-testid="increment-button"]').trigger('click')
|
||||
|
||||
expect(wrapper.emitted('change')).toHaveLength(1)
|
||||
expect(wrapper.emitted('change')[0]).toEqual([1])
|
||||
})
|
||||
```
|
||||
|
||||
## Using @testing-library/vue for Better Blackbox Tests
|
||||
|
||||
```javascript
|
||||
import { render, screen, fireEvent } from '@testing-library/vue'
|
||||
import Counter from './Counter.vue'
|
||||
|
||||
// Testing Library encourages accessible, user-centric queries
|
||||
test('increments counter on button click', async () => {
|
||||
render(Counter)
|
||||
|
||||
// Query by role - how screen readers see it
|
||||
const button = screen.getByRole('button', { name: /increment/i })
|
||||
const display = screen.getByText('0')
|
||||
|
||||
await fireEvent.click(button)
|
||||
|
||||
expect(screen.getByText('1')).toBeInTheDocument()
|
||||
})
|
||||
```
|
||||
|
||||
## What to Test vs What Not to Test
|
||||
|
||||
### DO Test (Public Interface)
|
||||
```javascript
|
||||
// Props affect rendered output
|
||||
test('shows title from props', () => {
|
||||
const wrapper = mount(Card, {
|
||||
props: { title: 'Hello World' }
|
||||
})
|
||||
expect(wrapper.text()).toContain('Hello World')
|
||||
})
|
||||
|
||||
// Slots render correctly
|
||||
test('renders slot content', () => {
|
||||
const wrapper = mount(Card, {
|
||||
slots: { default: '<p>Slot content</p>' }
|
||||
})
|
||||
expect(wrapper.text()).toContain('Slot content')
|
||||
})
|
||||
|
||||
// Emitted events
|
||||
test('emits close event when X clicked', async () => {
|
||||
const wrapper = mount(Modal)
|
||||
await wrapper.find('[data-testid="close-button"]').trigger('click')
|
||||
expect(wrapper.emitted('close')).toBeTruthy()
|
||||
})
|
||||
```
|
||||
|
||||
### DON'T Test (Implementation Details)
|
||||
```javascript
|
||||
// Don't test internal computed properties
|
||||
// Don't test internal methods
|
||||
// Don't test component options/setup internals
|
||||
// Don't test that specific child components are rendered (unless critical)
|
||||
// Don't rely exclusively on snapshot tests for correctness
|
||||
```
|
||||
|
||||
## Reference
|
||||
- [Vue.js Testing Guide](https://vuejs.org/guide/scaling-up/testing)
|
||||
- [Vue Test Utils - Testing Philosophy](https://test-utils.vuejs.org/guide/)
|
||||
- [Testing Library Guiding Principles](https://testing-library.com/docs/guiding-principles)
|
||||
Reference in New Issue
Block a user