测试和调试是应用开发中最重要的环节。一个经过充分测试的应用不仅能提供更好的用户体验,还能减少生产环境中的问题。鸿蒙提供了完整的测试框架和调试工具,帮助开发者编写高质量的代码。
本文将为你讲解鸿蒙应用的测试和调试方法,包括单元测试、集成测试、性能测试、调试工具等。通过学习这些内容,你将能够构建更加健壮和高效的应用。
单元测试
单元测试是测试的基础,用于验证单个函数或类的功能是否正确。
设置测试环境
在项目的build.gradle中添加测试依赖:
gradle
dependencies {
testImplementation 'junit:junit:4.13.2'
testImplementation 'org.mockito:mockito-core:4.0.0'
}
编写基础单元测试
typescript
// calculator.ts
export class Calculator {
add(a: number, b: number): number {
return a + b
}
subtract(a: number, b: number): number {
return a - b
}
multiply(a: number, b: number): number {
return a * b
}
divide(a: number, b: number): number {
if (b === 0) {
throw new Error('Division by zero')
}
return a / b
}
}
// calculator.test.ts
import { Calculator } from './calculator'
describe('Calculator', () => {
let calculator: Calculator
beforeEach(() => {
calculator = new Calculator()
})
test('should add two numbers correctly', () => {
expect(calculator.add(2, 3)).toBe(5)
expect(calculator.add(-1, 1)).toBe(0)
expect(calculator.add(0, 0)).toBe(0)
})
test('should subtract two numbers correctly', () => {
expect(calculator.subtract(5, 3)).toBe(2)
expect(calculator.subtract(0, 5)).toBe(-5)
})
test('should multiply two numbers correctly', () => {
expect(calculator.multiply(3, 4)).toBe(12)
expect(calculator.multiply(-2, 3)).toBe(-6)
})
test('should divide two numbers correctly', () => {
expect(calculator.divide(10, 2)).toBe(5)
expect(calculator.divide(7, 2)).toBe(3.5)
})
test('should throw error when dividing by zero', () => {
expect(() => calculator.divide(10, 0)).toThrow('Division by zero')
})
})
测试异步代码
typescript
// userService.ts
export class UserService {
async fetchUser(userId: number): Promise<any> {
let response = await fetch(`https://api.example.com/users/${userId}`)
return await response.json()
}
}
// userService.test.ts
import { UserService } from './userService'
describe('UserService', () => {
let userService: UserService
beforeEach(() => {
userService = new UserService()
})
test('should fetch user data', async () => {
// Mock fetch
global.fetch = jest.fn(() =>
Promise.resolve({
json: () => Promise.resolve({id: 1, name: 'John'})
})
)
let user = await userService.fetchUser(1)
expect(user.name).toBe('John')
expect(global.fetch).toHaveBeenCalledWith('https://api.example.com/users/1')
})
})
集成测试
集成测试用于验证多个组件之间的交互是否正确。
测试UI组件
typescript
// UserList.test.ts
import { render, screen, fireEvent } from '@testing-library/preact'
import { UserList } from './UserList'
describe('UserList Component', () => {
test('should render user list', async () => {
render(<UserList />)
// 等待异步数据加载
let userElements = await screen.findAllByRole('listitem')
expect(userElements.length).toBeGreaterThan(0)
})
test('should handle user selection', async () => {
render(<UserList />)
let firstUser = await screen.findByText('John')
fireEvent.click(firstUser)
// 验证选中状态
expect(firstUser).toHaveClass('selected')
})
test('should delete user when delete button is clicked', async () => {
render(<UserList />)
let deleteButton = await screen.findByRole('button', {name: /delete/i})
fireEvent.click(deleteButton)
// 验证用户被删除
expect(screen.queryByText('John')).not.toBeInTheDocument()
})
})
性能测试
性能测试用于验证应用的性能是否满足要求。
基准测试
typescript
// performance.test.ts
describe('Performance Tests', () => {
test('should process large array efficiently', () => {
let largeArray = Array.from({length: 100000}, (_, i) => i)
let startTime = performance.now()
let result = largeArray.filter(x => x % 2 === 0).map(x => x * 2)
let endTime = performance.now()
let duration = endTime - startTime
console.log(`Processing time: ${duration}ms`)
// 验证性能在可接受范围内
expect(duration).toBeLessThan(100)
expect(result.length).toBe(50000)
})
test('should render large list efficiently', async () => {
let startTime = performance.now()
render(<LargeList itemCount={10000} />)
let endTime = performance.now()
let duration = endTime - startTime
console.log(`Render time: ${duration}ms`)
expect(duration).toBeLessThan(1000)
})
})
内存泄漏检测
typescript
describe('Memory Leak Detection', () => {
test('should not leak memory when component unmounts', () => {
let initialMemory = (performance as any).memory?.usedJSHeapSize || 0
for (let i = 0; i < 100; i++) {
let component = render(<MyComponent />)
component.unmount()
}
// 强制垃圾回收(如果可用)
if (global.gc) {
global.gc()
}
let finalMemory = (performance as any).memory?.usedJSHeapSize || 0
let memoryIncrease = finalMemory - initialMemory
console.log(`Memory increase: ${memoryIncrease / 1024 / 1024}MB`)
// 验证内存增长在可接受范围内
expect(memoryIncrease).toBeLessThan(10 * 1024 * 1024) // 10MB
})
})
调试工具
DevEco Studio提供了强大的调试工具。
使用断点调试
typescript
// 在代码中设置断点
function calculateTotal(items: any[]): number {
let total = 0
for (let item of items) {
// 在这里设置断点,可以检查item的值
total += item.price * item.quantity
}
// 在这里设置条件断点,只在total > 1000时暂停
return total
}
使用日志调试
typescript
class Logger {
static debug(tag: string, message: string, data?: any): void {
console.log(`[DEBUG] ${tag}: ${message}`, data)
}
static info(tag: string, message: string, data?: any): void {
console.log(`[INFO] ${tag}: ${message}`, data)
}
static warn(tag: string, message: string, data?: any): void {
console.warn(`[WARN] ${tag}: ${message}`, data)
}
static error(tag: string, message: string, error?: any): void {
console.error(`[ERROR] ${tag}: ${message}`, error)
}
}
// 使用示例
class UserService {
async fetchUser(userId: number): Promise<any> {
Logger.debug('UserService', 'Fetching user', {userId})
try {
let response = await fetch(`https://api.example.com/users/${userId}`)
let user = await response.json()
Logger.info('UserService', 'User fetched successfully', {user})
return user
} catch (error) {
Logger.error('UserService', 'Failed to fetch user', error)
throw error
}
}
}
性能分析
typescript
class PerformanceMonitor {
private marks: Map<string, number> = new Map()
mark(name: string): void {
this.marks.set(name, performance.now())
}
measure(name: string, startMark: string, endMark: string): number {
let startTime = this.marks.get(startMark)
let endTime = this.marks.get(endMark)
if (!startTime || !endTime) {
throw new Error('Mark not found')
}
let duration = endTime - startTime
console.log(`${name}: ${duration.toFixed(2)}ms`)
return duration
}
}
// 使用示例
let monitor = new PerformanceMonitor()
monitor.mark('start')
// 执行某些操作
monitor.mark('end')
monitor.measure('Operation', 'start', 'end')
最佳实践
编写可测试的代码
typescript
// 不推荐:难以测试
class UserManager {
async getUser(userId: number): Promise<any> {
let response = await fetch(`https://api.example.com/users/${userId}`)
return await response.json()
}
}
// 推荐:易于测试
class UserManager {
constructor(private httpClient: HttpClient) {}
async getUser(userId: number): Promise<any> {
return await this.httpClient.get(`/users/${userId}`)
}
}
// 测试时可以注入mock的httpClient
let mockHttpClient = {
get: jest.fn(() => Promise.resolve({id: 1, name: 'John'}))
}
let userManager = new UserManager(mockHttpClient)
使用测试覆盖率工具
bash
# 运行测试并生成覆盖率报告
npm test -- --coverage
# 查看覆盖率报告
open coverage/index.html
持续集成
yaml
# .github/workflows/test.yml
name: Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: '16'
- run: npm install
- run: npm test
- run: npm run build
常见调试场景
调试网络请求
typescript
// 在浏览器开发者工具中查看网络请求
// 或使用代理工具如Charles或Fiddler
// 添加请求日志
class HttpClient {
async request(url: string, options: any): Promise<any> {
console.log('Request:', {url, options})
let response = await fetch(url, options)
let data = await response.json()
console.log('Response:', {url, status: response.status, data})
return data
}
}
调试状态管理
typescript
// 使用Redux DevTools或类似工具
class Store {
private state: any = {}
private listeners: Function[] = []
dispatch(action: any): void {
console.log('Action:', action)
// 更新状态
this.state = this.reducer(this.state, action)
console.log('New State:', this.state)
// 通知监听器
this.listeners.forEach(listener => listener(this.state))
}
private reducer(state: any, action: any): any {
// 状态更新逻辑
return state
}
}
总结
测试和调试是构建高质量应用的关键。关键要点包括:
- 编写单元测试验证单个函数的功能
- 编写集成测试验证组件之间的交互
- 进行性能测试确保应用性能
- 使用调试工具诊断问题
- 编写可测试的代码
- 使用测试覆盖率工具
- 建立持续集成流程
现在就在你的项目中应用这些最佳实践吧。如果你有任何问题或想法,欢迎在评论区分享。