阿里云主机折上折
  • 微信号
Current Site:Index > Unit testing specification

Unit testing specification

Author:Chuan Chen 阅读数:44032人阅读 分类: 前端综合

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

  1. 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>
}
  1. 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

  1. Props Naming: Use camelCase, with boolean props prefixed with is/has:
interface Props {
  isLoading: boolean
  maxLength?: number
  onComplete: () => void
}
  1. 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

  1. 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()
})
  1. 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

  1. Async Operation Testing:
test('should load data', async () => {
  jest.spyOn(api, 'fetchData').mockResolvedValue({ data: 'mock' })
  
  render(<DataFetcher />)
  await waitFor(() => {
    expect(screen.getByText('mock')).toBeInTheDocument()
  })
})
  1. 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

  1. Using React.memo:
const MemoComponent = React.memo(
  ({ data }) => <div>{data}</div>,
  (prev, next) => prev.data.id === next.data.id
)
  1. useCallback/useMemo:
function Parent() {
  const [count, setCount] = useState(0)
  const increment = useCallback(() => setCount(c => c + 1), [])
  
  return <Child onClick={increment} />
}

Bundle Size Control

  1. Code Splitting:
const HeavyComponent = React.lazy(() => import('./HeavyComponent'))

function App() {
  return (
    <Suspense fallback={<Spinner />}>
      <HeavyComponent />
    </Suspense>
  )
}
  1. 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

Front End Chuan

Front End Chuan, Chen Chuan's Code Teahouse 🍵, specializing in exorcising all kinds of stubborn bugs 💻. Daily serving baldness-warning-level development insights 🛠️, with a bonus of one-liners that'll make you laugh for ten years 🐟. Occasionally drops pixel-perfect romance brewed in a coffee cup ☕.