Unit testing specification
Component Development Standards
Component-based development is a core practice of front-end engineering. Well-designed components improve code reusability and reduce maintenance costs. The following outlines component development standards from the perspectives of design principles, directory structure, and code style.
Design Principles
- Single Responsibility Principle: Each component should handle only one specific functionality. For example, a button component should only manage click interactions and not include business logic:
// Bad: Mixing business logic
function OrderButton() {
const handleClick = () => {
fetch('/api/order').then(...)
}
return <button onClick={handleClick}>Submit</button>
}
// Good: Pure UI component
function Button({ onClick, children }) {
return <button onClick={onClick}>{children}</button>
}
- Controlled vs. Uncontrolled: Clearly distinguish between component state management methods. Form components typically need to support both modes:
// Controlled component
<input value={value} onChange={handleChange} />
// Uncontrolled component
<input defaultValue={initialValue} ref={inputRef} />
Directory Structure Standards
Recommended modular structure organized by functionality:
components/
Button/
index.tsx // Main component
style.module.css // Styles
types.ts // Type definitions
__tests__/ // Test directory
Button.test.tsx
Form/
Input/
index.tsx
validate.ts // Validation logic
Code Style Requirements
- Props Naming: Use camelCase, with boolean props prefixed with
is
/has
:
interface Props {
isLoading: boolean
maxLength?: number
onComplete: () => void
}
- Style Isolation: Recommend CSS Modules or Styled Components:
/* Button.module.css */
.primary {
background: var(--color-primary);
}
import styles from './Button.module.css'
function Button({ primary }) {
return (
<button className={primary ? styles.primary : ''}>
{children}
</button>
)
}
Unit Testing Standards
Unit testing is a critical aspect of ensuring component quality. Test coverage should at least include core functionality paths.
Testing Framework Configuration
Use Jest + Testing Library:
// jest.config.js
module.exports = {
testEnvironment: 'jsdom',
setupFilesAfterEnv: ['@testing-library/jest-dom'],
moduleNameMapper: {
'^@/(.*)$': '<rootDir>/src/$1'
}
}
Test Case Design Principles
- Behavior-Driven Testing: Focus on user perspective rather than implementation details:
// Bad: Testing internal state
test('should update state', () => {
render(<Counter />)
expect(screen.getByTestId('count').textContent).toBe('0')
})
// Good: Testing visible behavior
test('should increment count on click', async () => {
render(<Counter />)
await userEvent.click(screen.getByRole('button'))
expect(screen.getByText('1')).toBeInTheDocument()
})
- Edge Case Coverage: Include empty states, error handling, etc.:
describe('List component', () => {
it('should render empty state', () => {
render(<List items={[]} />)
expect(screen.getByText('No items found')).toBeVisible()
})
it('should handle null items', () => {
render(<List items={null} />)
expect(screen.getByText('Loading...')).toBeVisible()
})
})
Common Testing Patterns
- Async Operation Testing:
test('should load data', async () => {
jest.spyOn(api, 'fetchData').mockResolvedValue({ data: 'mock' })
render(<DataFetcher />)
await waitFor(() => {
expect(screen.getByText('mock')).toBeInTheDocument()
})
})
- Context Dependency Testing:
test('should use theme context', () => {
const theme = { color: 'red' }
render(
<ThemeProvider value={theme}>
<ThemedButton />
</ThemeProvider>
)
expect(screen.getByRole('button')).toHaveStyle({ color: 'red' })
})
Test Coverage Standards
Configure minimum thresholds in package.json
:
{
"jest": {
"coverageThreshold": {
"global": {
"branches": 80,
"functions": 85,
"lines": 90,
"statements": 90
}
}
}
}
Component Documentation Standards
Comprehensive documentation significantly reduces the cost of component usage. Recommended tools include Storybook or Docz.
Basic Documentation Structure
## Button
### Props
| Prop Name | Type | Default | Description |
|--------|------|--------|------|
| size | 'sm'\|'md'\|'lg' | 'md' | Button size |
### Code Example
```jsx
<Button size="lg" onClick={handleClick}>
Submit
</Button>
Interactive Demos
Define visual use cases in Storybook:
// Button.stories.tsx
export const Primary: Story = {
args: {
primary: true,
label: 'Button',
},
play: async ({ canvasElement }) => {
const canvas = within(canvasElement)
await userEvent.click(canvas.getByRole('button'))
}
}
Performance Optimization Requirements
Component performance directly impacts user experience. Follow these practices:
Rendering Optimization
- Using React.memo:
const MemoComponent = React.memo(
({ data }) => <div>{data}</div>,
(prev, next) => prev.data.id === next.data.id
)
- useCallback/useMemo:
function Parent() {
const [count, setCount] = useState(0)
const increment = useCallback(() => setCount(c => c + 1), [])
return <Child onClick={increment} />
}
Bundle Size Control
- Code Splitting:
const HeavyComponent = React.lazy(() => import('./HeavyComponent'))
function App() {
return (
<Suspense fallback={<Spinner />}>
<HeavyComponent />
</Suspense>
)
}
- Dependency Analysis:
npx source-map-explorer build/static/js/main.*.js
Error Handling Mechanisms
Robust components require comprehensive error boundaries and exception handling.
React Error Boundaries
class ErrorBoundary extends React.Component {
state = { hasError: false }
static getDerivedStateFromError() {
return { hasError: true }
}
componentDidCatch(error, info) {
logErrorToService(error, info.componentStack)
}
render() {
return this.state.hasError
? <FallbackUI />
: this.props.children
}
}
Async Error Catching
function AsyncComponent() {
const [state, setState] = useState()
useEffect(() => {
fetchData()
.then(setState)
.catch(error => {
showToast(error.message)
setState(null)
})
}, [])
return <div>{state?.data}</div>
}
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn