Landroid

[플러터] 테스트 본문

플러터

[플러터] 테스트

silso 2021. 1. 9. 22:18

플러터로 테스트와 디버깅하는 방법에 대해 설명하겠습니다.

 

테스트 종류

앱 테스트하기(플러터 공식문서)

  1. 유닛 테스트: 단위 기능, 방법 또는 클래스를 테스트합니다.
  2. 위젯 테스트 : 단일 위젯을 테스트합니다.
  3. 통합 테스트 : 완성된 앱이나 앱의 상당 부분을 테스트합니다.

 

1-1. 유닛 테스트

 

우선 pubspec.yaml 파일에서 dev_dependencies에 test 라이브러리를 추가합니다.

dev_dependencies:
  test: <latest_version>

그다음 테스트할 클래스나 메서드가 있으면 가져다가 테스트 코드에 작성하시면 됩니다.

저는 공식 페이지에 있는 예제를 그대로 가져다 쓰겠습니다.

// lib/counter.dart
class Counter {
  int value = 0;

  void increment() => value++;

  void decrement() => value--;
}
// test 패키지와 Counter 클래스를 import합니다.
import 'package:test/test.dart';
import 'package:counter_app/counter.dart';

void main() {
  test('해당 테스트에 관한 설명', () {
    final counter = Counter();

    counter.increment();

    expect(counter.value, 1);
  });
}

다트에서 유닛 테스트 코드는 test라는 함수에 설명을 넣을 수 있는 문자열테스트 코드를 동작시킬 함수를 인자로 받습니다.

 

그리고 전달받은 함수 안에 expect가 있는데 이 코드는 왼쪽 인자와 오른쪽 인자가 같은지를 비교하고 콘솔에 결과를 출력해주는 역할을 합니다.

 

이외에도 다트에서 여러 테스트 함수를 제공해줍니다.

  • test

    • 테스트에 대한 설명과 실제 테스트 코드를 적습니다.
    • 시간제한이나 테스트 환경 ( 브라우저, OS ) 등도 작성할 수 있습니다.
  • expect

    • 테스트의 기댓값과 실제값을 비교합니다.
  • setup

    • 테스트를 시작하기 전에 설정을 해줍니다.
    • 테스트 단위 하나마다 실행됩니다. ( test() 함수 하나가 테스트 단위 하나예요. 한 파일에 여러 test() 가 있으면 여러 번 실행됩니다. )
  • setupAll

    • 테스트 시작하기 전에 설정을 해줍니다.
    • 파일 하나에 한 번만 실행됩니다.
  • teardown

    • 테스트를 마치고 할 작업을 정해줍니다.
    • 테스트 단위 하나마다 실행됩니다 ( setup() 함수랑 동일합니다 )
  • teardownAll()

    • 테스트를 마치고 할 작업을 정해줍니다.
    • 파일 하나에 한번만 실행됩니다. ( setupAll() 함수랑 동일합니다 )

 

1-2. Mockito 사용하기

간혹 유닛 테스트의 한계 때문에 Mockito를 사용하기도 합니다.

Mockito는 해당 테스트가 특정 클래스에 의존하는 것을 방지하도록 도와주는 라이브러리입니다.

 

dependencies:
  http: <newest_version>
  # 예제를 위해 추가한 것이다.

dev_dependencies:
  test: <newest_version>
  mockito: <newest_version>
  # mockito는 여기에 추가하면 된다.

 

Future<Post> fetchPost(http.Client client) async {
  final String url = 'https://jsonplaceholder.typicode.com/posts/1';
  final response = await client.get(url);

  if (response.statusCode == 200) {
    // 요청이 성공하면 JSON으로 파싱
    return Post.fromJson(json.decode(response.body));
  } else {
    // 요청이 실패하면 에러 발생
    throw Exception('Failed to load post');
  }
}

http를 이용해서 만들어진 예제입니다. 이제 이걸 가지고 테스트할 겁니다.

 

// Mock 클래스로 MockClient를 생성
class MockClient extends Mock implements http.Client {}

main() {
  group('fetchPost', () {
    test('returns a Post if the http call completes successfully', () async {
      // 제공된 http.Client를 호출했을 때, 성공적인 응답을 반환하기 위해 Mockito를 사용
      final client = MockClient();
      when(client.get('https://jsonplaceholder.typicode.com/posts/1'))
          .thenAnswer((_) async => http.Response('{"title": "Test"}', 200));

      expect(await fetchPost(client), const TypeMatcher<Post>());
    });
    
    

    test('throws an exception if the http call completes with an error', () {
      // 실패 응답을 반환하기 위해 Mockito를 사용
      final client = MockClient();
      when(client.get('https://jsonplaceholder.typicode.com/posts/1'))
          .thenAnswer((_) async => http.Response('Not Found', 404));

      expect(fetchPost(client), throwsException);
    });
  });
}

Mockito는 위에 처럼 메서드의 반환 값을 임의로 지정해서 해당 메서드로부터 발생하는 의존성을 제거할 수 있습니다.

덕분에 반환 값을 지정하고 테스트 결과를 확인하기가 더 수월해집니다.

 

2-1. 위젯 테스트

위젯 테스트는 위젯 UI가 예상한 대로 보이고 상호 작용하는지 검증하는 것이다.

또 위젯 테스트는 여러 클래스를 포함하고 있어 위젯 생명주기 context를 갖춰진 테스트 환경이 필요하다.

 

 

dev_dependencies:
  flutter_test:
    sdk: flutter

이제 위젯을 한 번 만들어 봅시다!

class MyWidget extends StatelessWidget {
  final String title;
  final String message;

  const MyWidget(
    this.title,
    this.message,
    { Key key, }
  ) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      home: Scaffold(
        appBar: AppBar(
          title: Text(title),
        ),
        body: Center(
          child: Text(message),
        ),
      ),
    );
  }
}

그냥 아주 간단하게 제목과 화면 중앙에 띄울 메시지를 받는 위젯을 만들었습니다.

 

이제 이거 가지고 테스트해보죠.

void main() {
  testWidgets('MyWidget has a title and message', (WidgetTester tester) async {
    await tester.pumpWidget(MyWidget(title: 'T', message: 'M'));
  });
}

위 코드는 아주 기본적인 위젯 테스트 코드입니다.

 

testWidgets는 test()처럼 설명과 동작시킬 테스트 코드를 받습니다.

WidgetTester는 테스트 환경에서 위젯을 작성하고 상호 작용하기 위해 사용합니다.라고 공식문서에 써 있습니다.

pumpWidget은 tester가 위젯을 생성하기 위해 사용되는 함수입니다. 그래서 위젯 생성하는 시간을 기다리기 위해 async와 await를 사용하고 있습니다.

 

void main() {
  testWidgets('MyWidget has a title and message', (WidgetTester tester) async {
    await tester.pumpWidget(MyWidget(title: 'T', message: 'M'));

    final titleFinder = find.text('T');
    final messageFinder = find.text('M');
    
    expect(titleFinder, findsOneWidget);
    expect(messageFinder, findsOneWidget);
  });
}

find는 해당 조건에 맞춰 위젯을 찾아줍니다. 사용 방법은 Find Widgets 참고

findsOneWidget은 화면 상으로 한번 호출했는지 확인하기 위한 Matcher 클래스입니다.

이외에도

확인하기 위한 Matcher 클래스가 있습니다.

 

3-1. 통합 테스트

까지 하면 블로그 길이가 길어진 관계로 다음에 하겠습니다....

 

 

 

혹시 도움이 되셨다면 공감 한 번씩 눌러주세요~!

 

 

 

 

Comments