正文开始之前先看个笑话:
这是前几天小米发布会的PPT。你说吧,就这种大厂这么重要的场合下如此重要的数据都能搞错,借用网友的一句话:几十页的PPT都能搞大错,几万行的澎湃OS出几个bug又怎么啦?[狗头]
咱们不写测试岂不是天经地义?但话又说回来:就算我不写,但我不能不会啊! 3分钟快速入门~
目录
- 基础测试概念
- 常用测试场景
- 高级测试技巧
- 最佳实践
- 常见问题解决
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 测试原则
- 每个测试只测试一个概念
- 避免测试依赖
- 保持测试简单明了
- 适当使用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('不满足测试条件');
}
});
}
总结
有效的测试策略应该:
- 从简单开始,逐步增加复杂度
- 关注代码的关键路径
- 考虑边界情况
- 保持测试代码的可维护性
- 定期运行和维护测试
记住,好的测试不仅能保证代码质量,还能作为代码的文档和示例,帮助其他开发者理解代码的用法和意图。