본문 바로가기

Flutter

test_package.expect() 분석

0. 목표

Text.data 의 값이 'H' 인 Widget을 찾아 Widget Tree에 존재하는지 확인한다.

 

void main(){
  testWidgets('find a Text widget', (WidgetTester tester) async {
    await tester.pumpWidget(const MaterialApp(
      home: Scaffold(
        body: Text('H'),
      ),
    ));

    expect(find.text('H'), findsOneWidget);
  });
}

 

 

expect(Finder, Matcher)

 

expect(find.text('H'), findsOneWidget);

 

 

1. Finder

Type Argument 설명
Finder find.text('H') Text나 EditableText의 data field 가 'H'인 Widget을 찾아 Finder 로 반환한다.

 

finders.dart

 

Finder text(String text, { bool skipOffstage = true }) 
  => _TextFinder(text, skipOffstage: skipOffstage);
    
class _TextFinder extends MatchFinder {
  _TextFinder(this.text, { bool skipOffstage = true }) : super(skipOffstage: skipOffstage);

  final String text;

  @override
  String get description => 'text "$text"';

  @override
  bool matches(Element candidate) {
    final Widget widget = candidate.widget;
    if (widget is Text) {
      if (widget.data != null)
        return widget.data == text;
      assert(widget.textSpan != null);
      return widget.textSpan!.toPlainText() == text;
    } else if (widget is EditableText) {
      return widget.controller.text == text;
    }
    return false;
  }
}

 

_TextFinder 는 MatchFinder 를 상속받는다. matches는 Widget의 String 값(Text 는 data, EditableText는 controller의 text) 을 비교후 true/false를 반환한다.

 

 

2. Matcher

Type function 설명
Matcher _findsWidgetMatcher(min, max) 모든 matcher의 base 클래스
- matches, describe 는 필수 구현 클래스

bool matches( covariant Finder finder,
                         Map<dynamic, dynamic> matchState);

 

class _FindsWidgetMatcher extends Matcher {
  const _FindsWidgetMatcher(this.min, this.max);

  final int? min;
  final int? max;

  @override
  bool matches(covariant Finder finder, Map<dynamic, dynamic> matchState) {
    assert(min != null || max != null);
    assert(min == null || max == null || min! <= max!);
    matchState[Finder] = finder;
    int count = 0;
    final Iterator<Element> iterator = finder.evaluate().iterator;
    if (min != null) {
      while (count < min! && iterator.moveNext())
        count += 1;
      if (count < min!)
        return false;
    }
    if (max != null) {
      while (count <= max! && iterator.moveNext())
        count += 1;
      if (count > max!)
        return false;
    }
    return true;
  }
  
  ...
}​

 

입력받은 Finder 의 element의 개수를 확인한다. findsOneWidget(1, 1) 은 Finder에 Widget이 하나만 있을 때 true 를 반환한다.

 

 

3. expect()

 

목적 : Actual 이 Matcher.matches() 를 만족하는지 확인한다.

 

expect()는 widget_tester.dart에 구현되어있고, 이 expect() 는 test_package.expect()를 호출한다.

 

test_package.expert()

 

void expect(actual, matcher,
    {String? reason,
    skip,
    @Deprecated('Will be removed in 0.13.0.') bool verbose = false,
    @Deprecated('Will be removed in 0.13.0.') ErrorFormatter? formatter}) {
  _expect(actual, matcher,
      reason: reason, skip: skip, verbose: verbose, formatter: formatter);
}


Future _expect(actual, matcher,
    {String? reason, skip, bool verbose = false, ErrorFormatter? formatter}) {
  formatter ??= (actual, matcher, reason, matchState, verbose) {
  
  ... 생략
  
  var matchState = {};
  try {
    if ((matcher as Matcher).matches(actual, matchState)) {
      return Future.sync(() {});
    }
  } catch (e, trace) {
    reason ??= '$e at $trace';
  }
  fail(formatter(actual, matcher as Matcher, reason, matchState, verbose));
}

 

_expect() 의 내부구현을 보면 matcher가 Matcher의 인스턴스인 경우 Matcher.matches(Finder, matchState) 를 호출한다.

- Finder : 문자 'H'를 포함하는 Widget (find.text('H'))

- Matcher : Finder에서 찾은 Widget의 개수를 확인한다. (Matcher findsOneWidget = _FindWidgetMatcher(1, 1);)

 

Matcher.matches() 로 확인해 원하는 결과가 나오는지 확인한다. Widget이 1개라면 true를 반환하고 테스트 케이스를 통과한다. 그렇지 않으면 false를 반환하게되고 테스트 결과가 실패로 나온다.

 

'Flutter' 카테고리의 다른 글

dart 유닛 테스트  (0) 2021.07.12
Flutter Widget Test  (0) 2021.07.04
Null Safety  (0) 2021.06.02