公告

👇欢迎大家关注公众号&私信交流👇

Skip to content

Flutter 测试覆盖率详解

简介

测试覆盖率是衡量测试完整性的重要指标。本文介绍如何在 Flutter 中统计和分析测试覆盖率。

基本配置

添加依赖

yaml
dev_dependencies:
  flutter_test:
    sdk: flutter
  coverage: ^1.6.3

运行命令

bash
# 运行测试并收集覆盖率
flutter test --coverage

# 生成 HTML 报告
genhtml coverage/lcov.info -o coverage/html

覆盖率类型

1. 行覆盖率

dart
// 统计每行代码是否被执行
@override
Widget build(BuildContext context) {
  if (condition) {  // 已覆盖
    return Widget1();  // 已覆盖
  } else {
    return Widget2();  // 未覆盖
  }
}

2. 分支覆盖率

dart
String getMessage(int value) {
  if (value > 0) {  // true 分支已覆盖
    return 'Positive';
  } else if (value < 0) {  // false 分支未覆盖
    return 'Negative';
  } else {  // 未覆盖
    return 'Zero';
  }
}

完整示例

dart
// 被测试的类
class Calculator {
  int add(int a, int b) => a + b;
  int subtract(int a, int b) => a - b;
  int multiply(int a, int b) => a * b;
  
  double divide(int a, int b) {
    if (b == 0) {
      throw ArgumentError('Cannot divide by zero');
    }
    return a / b;
  }
  
  bool isEven(int number) => number % 2 == 0;
  
  String getNumberType(int number) {
    if (number > 0) {
      return 'Positive';
    } else if (number < 0) {
      return 'Negative';
    } else {
      return 'Zero';
    }
  }
}

// 测试文件
void main() {
  group('Calculator', () {
    late Calculator calculator;
    
    setUp(() {
      calculator = Calculator();
    });
    
    group('add', () {
      test('should add two positive numbers', () {
        expect(calculator.add(2, 3), equals(5));
      });
      
      test('should add positive and negative numbers', () {
        expect(calculator.add(2, -3), equals(-1));
      });
    });
    
    group('divide', () {
      test('should divide two numbers', () {
        expect(calculator.divide(6, 2), equals(3));
      });
      
      test('should throw error when dividing by zero', () {
        expect(
          () => calculator.divide(6, 0),
          throwsA(isA<ArgumentError>()),
        );
      });
    });
    
    group('getNumberType', () {
      test('should return Positive for positive numbers', () {
        expect(calculator.getNumberType(5), equals('Positive'));
      });
      
      test('should return Negative for negative numbers', () {
        expect(calculator.getNumberType(-5), equals('Negative'));
      });
      
      test('should return Zero for zero', () {
        expect(calculator.getNumberType(0), equals('Zero'));
      });
    });
  });
}

覆盖率报告

1. 命令行报告

bash
# 显示覆盖率摘要
flutter test --coverage --coverage-path=coverage/lcov.info

# 生成详细报告
genhtml coverage/lcov.info -o coverage/html

2. HTML 报告

html
<!DOCTYPE html>
<html>
<head>
  <title>Coverage Report</title>
</head>
<body>
  <div class="coverage-report">
    <h1>Coverage Report</h1>
    <div class="summary">
      <p>Line Coverage: 85%</p>
      <p>Branch Coverage: 75%</p>
      <p>Function Coverage: 90%</p>
    </div>
    <!-- 详细报告内容 -->
  </div>
</body>
</html>

CI/CD 集成

GitHub Actions

yaml
name: Test Coverage

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

jobs:
  test:
    runs-on: ubuntu-latest
    
    steps:
    - uses: actions/checkout@v2
    
    - uses: subosito/flutter-action@v2
      with:
        flutter-version: '3.x'
    
    - name: Install dependencies
      run: flutter pub get
    
    - name: Run tests with coverage
      run: flutter test --coverage
    
    - name: Upload coverage reports
      uses: codecov/codecov-action@v3
      with:
        file: coverage/lcov.info

最佳实践

  1. 设置覆盖率目标
dart
// 在 CI 中设置最低覆盖率要求
final minimumCoverage = 80.0;
final coverage = calculateCoverage();
if (coverage < minimumCoverage) {
  throw Exception('Coverage below minimum threshold');
}
  1. 忽略不需要测试的代码
dart
// coverage:ignore-start
void debugLog(String message) {
  print(message);
}
// coverage:ignore-end
  1. 定期检查覆盖率趋势
dart
class CoverageTrend {
  final DateTime date;
  final double coverage;
  
  CoverageTrend(this.date, this.coverage);
  
  static void trackCoverage(double coverage) {
    // 记录覆盖率历史
  }
}

注意事项

  1. 合理设置覆盖率目标
  2. 关注关键代码覆盖
  3. 避免为覆盖率而测试
  4. 定期更新测试用例
  5. 维护测试质量

总结

测试覆盖率是衡量测试质量的重要指标,但不是唯一指标。通过合理的覆盖率统计和分析,可以帮助我们发现测试盲点,提高代码质量。