플러터 테스트, 배포

LeeWonjin·2024년 5월 7일
0

플러터

목록 보기
13/15

테스트

flutter pub add dev:test dev:flutter_test dev:mockito dev:build_runner 'dev:integration_test:{"sdk":"flutter"}'

세 가지 종류

  • 유닛 테스트 : 단일 함수, 클래스 단위에 대해서
  • 위젯(컴포넌트) 테스트 : 단일 위젯에 대해서
  • 통합 테스트 : 앱 전체 또는 많은 부분에 대해서

조금 다른 이야기로

  • CI(지속적 통합) : 새로운 코드 변경사항이 있을 때 테스트 자동 실행

유닛 테스트

어떻게 함?

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);
  });
}

실행 하려면

  • 터미널에서는
    • 모든 test에 대해 flutter test
    • 특정 파일 test에 대해 flutter test test/테스트파일_이름.dart
    • 특정 group에 대해 flutter test --plain-name "그룹 이름"
  • vscode에서는
    • 우측 상단 디버깅 버튼 (run - start debugging)

틀리면 난리법석을 떤다

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>

Mock 웹서비스, DB가 필요하다면

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(duration)
    • 참고 : 애니메이션을 시작하려면 duration인수 없이 그냥 tester.pump()를 최초에 한 번 호출해줘야된다. 그래야 ticker가 굴러간다.
  • tester.pumpAndSettle()

검증

expect의 두 번째 인수는 Matchers다.
findOneWidget은 Matcher의 일종. 자주 쓸만한 매쳐는 아래와 같다.

  • findsNothing : 그런거 없다
  • findOneWidget : 니가 말한 위젯. 딱 1개 찾았다
  • findsWidgets : 니가 말한 위젯. 1개 이상 찾았다
  • findsNWidgets : 니가 지정한 위젯과 숫자 N에 대해서. N개 위젯 찾았다
  • matchesGlodenFile : 니가 말한 비트맵 이미지. 잘 렌더링 됐다.

시뮬레이션

요소가 존재하는지, 일치하는지 검증할 수 있을 뿐 아니라
스크롤링과 유저상호작용의 시뮬레이션도 가능하다.

탭하는 예시

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>

Flavors

https://docs.flutter.dev/deployment/flavors

하나의 앱이 여러 가지 버전을 가지도록 할 수 있음
e.g., 무료버전, 유료버전, 개발용 버전 -- 각각을 플레이버라고 한다.

안드로이드, ios 각각 플랫폼 설정을 만져서 플레이버를 설정할 수 있음.
개별의 앱을 개발하지 않고서도.

Android

https://docs.flutter.dev/deployment/android

iOS

https://docs.flutter.dev/deployment/ios

profile
노는게 제일 좋습니다.

0개의 댓글

관련 채용 정보