flutter 통합테스트하기

su.log·2022년 8월 21일
0

통합테스트

  • 통합 테스트란 UI가 개발자나 기획자가 의도한대로 작동하는지 확인하기 위해서 프로그래밍적으로 UI를 가 시화해서 진행하는 테스트입니다 .
  • UI가 예상대로 보여지는지 상호작용이 정상적으로 동작하는지 확인하기 위해 진행합니다.
  • 앱을 구동하여 앱 전체를 전반적으로 테스트 해볼 수 있어요
  • 실제 UI를 그리며 동작해서 확인하기 편해요

로그인 통합테스트 진행

  • 테스트 진행 전에 pubspec.yaml 파일의 dev_dependencies에 integration_test를 활성화 시켜줍니다.

  • lib과 같은 위치에 integration_test 디렉토리를 생성하고 내부에 login_test.dart 파일을 생성합니다.
  • 제가 예제로 진행한 테스트는 기그앱 시작화면부터 회원가입 후 근무등록을 테스트를 해보았습니다. 순서는 main.dart 부터 시작하여 progress_sign_up.dart에서 회원가입 완료 후 계정생성 → 마케팅 팝업 종료 → 근무지생성 → 근무등록까지 입니다.
  • 아래는 테스트파일 코드 입니다.
// @dart=2.9
import 'dart:math';

import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:model/user.dart';
import 'package:integration_test/integration_test.dart';
import 'package:mockito/mockito.dart';
import 'package:main.dart' as app;

class MockNavigatorObserver extends Mock implements NavigatorObserver {}

void main() {
  IntegrationTestWidgetsFlutterBinding.ensureInitialized();
  String email = '${DateTime.now().millisecondsSinceEpoch.toString()}' + '@test.com';
  String password = '123123';
  String name = 'test' +'${Random().nextInt(100)+1}';

  setUp(() async {
    User.ClearLocalData();
  });

  final observerMock = MockNavigatorObserver();
  group('worker_login_test', () {
    testWidgets('authentication Testing', (WidgetTester tester) async {
      final originalOnError = FlutterError.onError;
      FlutterError.onError = (FlutterErrorDetails details) {
        originalOnError(details);
        print('details ${details}');
      };
      print("STARTING authentication");
      await app.main();
      await tester.pumpWidget(LaunchApp(observer: observerMock));
      await tester.pumpAndSettle(const Duration(seconds: 1), EnginePhase.sendSemanticsUpdate, Duration(seconds: 10))
      var listView = find.byKey(Key('worker intro select page'));
      expect(listView, findsOneWidget);
      print('리스트뷰![](https://velog.velcdn.com/images/poer611/post/55c5ed87-1de2-4ffc-90f5-f37d2f7eb33e/image.png)
 찾기');
      var button = find.byKey(Key('existing user login'));
      expect(button, findsOneWidget);
      print('기존 유저 로그인 버튼 찾기');
      await tester.tap(find.byKey(Key('enter login page')));
      print('기존 유저 로그인 버튼 탭');
      await tester.pump(const Duration(milliseconds: 500));
      var container = find.byKey(Key('signIntro container'));
      expect(container, findsOneWidget);
      print('이메일 입력 페이지 도착');
      var loginDes = find.byKey(Key('login text'));
      expect(loginDes, findsOneWidget);
      print('회원가입 혹은 로그인을 해주세요');
      await tester.enterText(find.byType(TextField), email);
      print('이메일 입력 완료');
      var loginButton = find.byKey(Key('email Input Button'));
      expect(loginButton, findsOneWidget);
      print('이메일 확인 버튼 찾기');
      await tester.tap(loginButton);
      print('이메일확인 버튼 탭');
      await tester.pump(const Duration(milliseconds: 1000));
      var registerColumn = find.byKey(Key('register complete ui'));
      expect(registerColumn, findsOneWidget);
      print('회원가입 페이지 도착');
      var emailCheckText = find.byKey(Key('email check text'));
      expect(emailCheckText, findsOneWidget);
      print('이메일 체크 텍스트 확인');
      await tester.enterText(find.byKey(Key('input password')), password);
      print('패스워드 입력');
      await tester.enterText(find.byKey(Key('check the password')), password);
      print('패스워드 재확인');
      await tester.enterText(find.byKey(Key('input register name')), name);
      print('이름 입력');
      var registerButton = find.byKey(Key('complete register button'));
      expect(registerButton, findsOneWidget);
      print('회원가입 완료버튼 찾기');
      await tester.tap(registerButton);
      print('회원가입 완료');
      for (var i = 0; i < 15; i ++) {
        print("앱 대기 $i");
        await tester.pump(Duration(milliseconds: 500));
      }
      print("앱 시작");
}
  • 테스트 명령어는 터미널에 ‘flutter test 디렉토리명/파일명’ 을 입력하시면 실행됩니다.

  • Integration_test를 위해 필수로 설정해야할 것

    • main()에 IntegrationTestWidgetsFlutterBinding.ensureIntialized();를 적어줍니다.
      • Flutter Engine과 상호작용하여 테스트를 기기에서 실행시키기 위해 사용합니다.
    • main.dart 를 testWidget() 내부에 호출해 줍니다.
  • setUp()

    테스트가 실행되기 전에 등록된 함수를 호출합니다.

  • setUpAll()

    현재 group내에서 테스트 케이스 전에 매번 호출합니다.

  • tearDown()

    현재 범위의 테스트가 완료된 후 무엇인가를 실행하는데 사용합니다. 일반적으로는 인스턴스를 삭제하거나 구독을 취소하는데 사용합니다.

  • tearDownAll()

    • group()내에서 테스트 케이스 후에 매번 호출됩니다.
  • Tap, enterText, drag

    • await tester.tap(find.byKey..text..등등) → 해당 위젯을 탭합니다.
      네비게이터나 애니메이션으로 ui가 변경되는 경우라면 await tester.pump()나 pumpAndSettle()을 함께 써주어야 합니다.
    • await tester.enterText(find.byType(TextField), email) → 해당 위젯에 임의의 텍스트를 작성해줍니다.
    • await tester.drag(find.byType(Dismissible), const Offset(500.0, 0.0)); → swipe를 할때 사용합니다.
  • await tester.pump() → 일정시간이 지난후 프레임을 트리거하는 메소드(ui를 새로 그려줄때 사용)로 duration을 줄 수 있어요. pump()는 한개의 Frame만 트리거 해줘요

  • await tester.pumpAndSettle() → 더 이상 예약된 프레임이 없을때까지 주어진 duration동안 pump()를 반복 호출하는 메서드.
    마찬가지로 duration을 줄 수 있고 애니매이션을 그릴때 사용해요.

  • 아래의 코드를 사용하는 이유는?

    • 테스트 케이스 실행 중 의미를 알 수 없는 _PendingDetai~~..l이라는 에러가 날 때가 있는데 아래의 코드를 작성해주면 에러의 자세한 내용을 표시해줍니다.
final originalOnError = FlutterError.onError;
      FlutterError.onError = (FlutterErrorDetails details) {
        originalOnError(details);
        print('details ${details}');
      };
      print("STARTING authentication");
  • 주의사항!
    • 통합테스트는 ui적 에러까지 잡기때문에 만약 화면에서 Overflow가 난다거나하는 경우에도 테스트가 Failed로 끝나게 됩니다.

0개의 댓글