flutter pub add dev:test dev:flutter_test dev:mockito dev:build_runner 'dev:integration_test:{"sdk":"flutter"}'
세 가지 종류
조금 다른 이야기로
test()
메소드를 사용한다.
test('테스트 이름', () {
// 뭔가 작업하기
final 인스턴스 = 클래스();
인스턴스.테스트할_메소드();
// 결과 검증해보기
expect(인스턴스.테스트할_멤버, 기대값);
});
테스트할거 많아지면 복잡하니까 group()
으로 test()
를 묶어놓을 수도 있다.
group('그룹 이름', () {
test( ... ),
test( ... ),
...
test( ... ),
});
test코드를 아래와 같은 디렉토리 구조로 생성한다.
프로젝트 루트
ㄴ /lib
ㄴ counter.dart
ㄴ /test
ㄴ counter_test.dart
그리고 이런식으로 코드를 짠다.
// /lib/counter.dart
class Counter {
int n = 0;
void up() => n++;
void down() => n--;
}
// /test/counter_test.dart
import 'package:우리프로젝트/counter.dart';
import 'package:test/test.dart';
void main() {
test('초기값은 0이어야 하는 것이에요', () {
final ctr = Counter();
expect(ctr.n, 0);
});
test('두 번 up하면 2어야 하는 것이에요', () {
final ctr = Counter();
ctr.up();
ctr.up();
expect(ctr.n, 2);
});
}
flutter test
flutter test test/테스트파일_이름.dart
flutter test --plain-name "그룹 이름"
틀리면 난리법석을 떤다
flutter test
00:02 +0 -1: /home/wonjin/projects/flutter-study/docs/test/counter_test.dart:
초기값은 0이어야 하는 것이에요 [E]
Expected: <100000>
Actual: <0>
package:matcher expect
test/counter_test.dart 7:5 main.<fn>
http통신 목 클라이언트 자동생성 예제
https://docs.flutter.dev/cookbook/testing/unit/mocking
테스트 파일의 함수에 @GenerateMocks()를 붙이고
import '필요한 패키지';
import 'package:mocking/main.dart';
import 'package:mockito/annotations.dart';
([필요한거])
void main() {}
아래 명령을 입력해 코드 자동생성
dart run build_runner build
해서 테스트를 돌리면 정신건강에 도움이 된다.
https://docs.flutter.dev/cookbook/testing/widget/introduction
위젯을 테스트하려면
testWidget('테스트 이름', (tester) async {
// 테스트할 위젯 렌더링
await tester.pumpWidget(const 위젯(name:'wonjin'));
// 나올 것으로 기대되는 요소 찾기
final nameFinder = find.text('wonjin');
// 기대되는 그 요소, 딱 1개 있냐?
expect(nameFinder, findsOneWidget);
});
pumpWidget은 위젯 불러와서 빌드하고 렌더링한다
근데 상태 바뀐다고 해서 재빌드 해주지 않으니까 아래 메소드를 써서 재빌드 시킨다.
tester.pump()
를 최초에 한 번 호출해줘야된다. 그래야 ticker가 굴러간다.expect의 두 번째 인수는 Matchers다.
findOneWidget
은 Matcher의 일종. 자주 쓸만한 매쳐는 아래와 같다.
요소가 존재하는지, 일치하는지 검증할 수 있을 뿐 아니라
스크롤링과 유저상호작용의 시뮬레이션도 가능하다.
탭하는 예시
testWidget('이름', (tester) async {
await tester.tap(find.byType(뭐시기 위젯));
await tester.pump();
expect(검증을 하자);
});
스크롤하는 예시
...
await tester.scrollUntilVisible()
expect(검증을 하자)
...
root/integration_test
디렉토리에 테스트 파일 만든다
프로젝트 루트
ㄴ /lib
ㄴ main.dart
ㄴ /integration_test
ㄴ app_test.dart
대충 이렇게 테스트 쓴다
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import 'package:프로젝트경로/main.dart';
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
group('테스트를 하자', () {
testWidgets('위젯들을 테스트하자', (tester) async {
await tester.pumpWidget(위젯);
...
expect(find.text('1'), 매쳐);
});
});
}
테스트 실행은
flutter test 프로젝트경로/테스트파일.dart
split debug info의 디렉토리 인수에 심볼맵 저장
obfuscate플래그 없이도 split debug info플래그 붙일 수 있음.
심볼맵을 뽑을 목적이기도 하지만, 코드 사이즈를 줄이는 역할도 하기 때문.
flutter build apk --obfuscate --split-debug-info=/<project-name>/<directory>
이후 심볼맵으로 난독화된 내용을 읽을 수 있게 바꾸려면
(안드로이드에 대해)
# 스택트레이스
flutter symbolize -i <stack trace file> -d out/android/app.android-arm64.symbols
# 코드
flutter build apk --obfuscate --split-debug-info=/<project-name>/<directory> --extra-gen-snapshot-options=--save-obfuscation-map=/<your-path>
https://docs.flutter.dev/deployment/flavors
하나의 앱이 여러 가지 버전을 가지도록 할 수 있음
e.g., 무료버전, 유료버전, 개발용 버전 -- 각각을 플레이버라고 한다.
안드로이드, ios 각각 플랫폼 설정을 만져서 플레이버를 설정할 수 있음.
개별의 앱을 개발하지 않고서도.
https://docs.flutter.dev/deployment/android