flutter/dart中你可能会用到测试?

300 阅读3分钟

正文开始之前先看个笑话:

image.png 这是前几天小米发布会的PPT。你说吧,就这种大厂这么重要的场合下如此重要的数据都能搞错,借用网友的一句话:几十页的PPT都能搞大错,几万行的澎湃OS出几个bug又怎么啦?[狗头]

咱们不写测试岂不是天经地义?但话又说回来:就算我不写,但我不能不会啊! 3分钟快速入门~

目录

  1. 基础测试概念
  2. 常用测试场景
  3. 高级测试技巧
  4. 最佳实践
  5. 常见问题解决

1. 基础测试概念

1.1 为什么需要测试?

  • 保证代码质量
  • 防止回归问题
  • 提供代码文档
  • 促进好的代码设计

1.2 测试的基本结构

void main() {
  test('测试名称', () {
    // 准备数据
    final calculator = Calculator();
    
    // 执行操作
    final result = calculator.add(2, 3);
    
    // 验证结果
    expect(result, equals(5));
  });
}

2. 常用测试场景

2.1 分组测试

void main() {
  group('计算器测试', () {
    late Calculator calculator;
    
    setUp(() {
      calculator = Calculator();
    });

    test('加法', () {
      expect(calculator.add(2, 3), equals(5));
    });

    test('减法', () {
      expect(calculator.subtract(5, 3), equals(2));
    });
  });
}

2.2 异步测试

void main() {
  test('异步操作测试', () async {
    final data = await fetchData();
    expect(data, isNotNull);
  });

  test('超时测试', () async {
    expect(
      Future.delayed(Duration(seconds: 2)),
      completes,
    );
  }, timeout: Timeout(Duration(seconds: 3)));
}

2.3 异常测试

void main() {
  test('异常测试', () {
    expect(
      () => divideByZero(),
      throwsA(isA<DivisionByZeroException>())
    );
  });
}

2.4 Stream测试

void main() {
  test('Stream测试', () async {
    final stream = Stream.fromIterable([1, 2, 3]);
    
    expect(
      stream,
      emitsInOrder([
        1,
        2,
        3,
        emitsDone,
      ])
    );
  });
}

2.5 Mock测试

class MockApi extends Mock implements Api {}

void main() {
  late MockApi mockApi;
  late UserService userService;

  setUp(() {
    mockApi = MockApi();
    userService = UserService(mockApi);
  });

  test('获取用户信息', () async {
    when(mockApi.getUser(1))
        .thenAnswer((_) async => User(id: 1, name: 'Test'));

    final user = await userService.getUser(1);
    expect(user.name, equals('Test'));
  });
}

3. 高级测试技巧

3.1 参数化测试

void main() {
  final testCases = {
    'empty string': {'input': '', 'expected': 0},
    'single word': {'input': 'hello', 'expected': 1},
    'multiple words': {'input': 'hello world', 'expected': 2},
  };

  testCases.forEach((description, data) {
    test('Word count: $description', () {
      final result = countWords(data['input'] as String);
      expect(result, equals(data['expected']));
    });
  });
}

3.2 性能测试

void main() {
  test('性能测试', () {
    final stopwatch = Stopwatch()..start();
    
    // 执行待测代码
    final result = complexOperation();
    
    stopwatch.stop();
    expect(stopwatch.elapsedMilliseconds, lessThan(100));
  });
}

3.3 平台特定测试

void main() {
  group('平台特定功能', () {
    if (Platform.isIOS) {
      test('iOS专属功能', () {
        // iOS测试代码
      });
    }

    if (Platform.isAndroid) {
      test('Android专属功能', () {
        // Android测试代码
      });
    }
  });
}

4. 最佳实践

4.1 测试文件组织

lib/
  src/
    calculator.dart
test/
  unit/
    calculator_test.dart
  integration/
    api_test.dart
  widget/
    login_page_test.dart

4.2 命名约定

  • 测试文件:*_test.dart
  • 测试描述:清晰表达测试意图
  • 变量命名:具有描述性

4.3 测试原则

  1. 每个测试只测试一个概念
  2. 避免测试依赖
  3. 保持测试简单明了
  4. 适当使用setUp和tearDown

5. 常见问题解决

5.1 异步测试超时

void main() {
  test('长时间运行的测试', () async {
    // 增加超时时间
    await longRunningOperation();
  }, timeout: Timeout(Duration(minutes: 2)));
}

5.2 测试清理

void main() {
  late File testFile;
  
  setUp(() async {
    testFile = File('test.txt');
    await testFile.writeAsString('test data');
  });

  tearDown(() async {
    if (await testFile.exists()) {
      await testFile.delete();
    }
  });

  test('文件操作', () async {
    expect(await testFile.readAsString(), equals('test data'));
  });
}

5.3 条件测试

void main() {
  test('特定条件测试', () {
    if (someCondition) {
      // 执行测试
    } else {
      skip('不满足测试条件');
    }
  });
}

总结

有效的测试策略应该:

  1. 从简单开始,逐步增加复杂度
  2. 关注代码的关键路径
  3. 考虑边界情况
  4. 保持测试代码的可维护性
  5. 定期运行和维护测试

记住,好的测试不仅能保证代码质量,还能作为代码的文档和示例,帮助其他开发者理解代码的用法和意图。

相关资源