일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- SOLID 원칙
- 에러 메시지를 잘보자 ^^
- retorift
- 안드로이드
- TDD 개발 방법론
- Flutter
- 다트 테스트
- pubspec
- Android
- permission_handler
- Refresh Tocken
- 안드로이드를 위한
- 2D 그래픽 라이브러리
- refresh 토큰
- 다트
- pubspec.yaml
- 8시간 삽질
- 토큰갱신
- 배움순서
- 객체 지향 설계
- 테스트 주도 개발론
- dart
- dart test
- 플러터 테스트
- 인코딩방지
- Same parameter
- Parameter specified as non-null is null
- 플러터
- 2D graphics library
- widget test
- Today
- Total
Landroid
[플러터] 테스트 본문
플러터로 테스트와 디버깅하는 방법에 대해 설명하겠습니다.
테스트 종류
- 유닛 테스트: 단위 기능, 방법 또는 클래스를 테스트합니다.
- 위젯 테스트 : 단일 위젯을 테스트합니다.
- 통합 테스트 : 완성된 앱이나 앱의 상당 부분을 테스트합니다.
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 클래스입니다.
이외에도
- findsNothing 한 번도 안 호출했는지
- findsWidgets 여러 번 호출했는지
- findsNWidgets N번만큼 호출했는지
확인하기 위한 Matcher 클래스가 있습니다.
3-1. 통합 테스트
까지 하면 블로그 길이가 길어진 관계로 다음에 하겠습니다....
혹시 도움이 되셨다면 공감 한 번씩 눌러주세요~!
'플러터' 카테고리의 다른 글
[Flutter] 플러터 프로젝트에 .gitignore 추가하기 (1) | 2021.03.10 |
---|---|
FutureBuilder에서 future 함수 중복 호출 방지 (0) | 2021.02.21 |
[Flutter] Skia가 뭐지? (0) | 2021.01.15 |
[플러터] 헷갈리는 권한 정리(with permission_handler) (0) | 2020.12.22 |
[플러터] Stateful 생명주기 (2) | 2020.12.17 |