Dart - Test 작성하기(test package)
Dart - Test 작성하기(test package)
dart의 test package를 사용하여 테스트를 작성하는 방법에 대하여 알아보도록 하겠습니다
목차
test package
https://pub.dev/packages/test#writing-tests
test package는 Dart에서 테스트를 작성하고 실행하는 표준 방법을 제공합니다.
테스트 실행하기
1. 단일 파일 테스트 실행하기
pub run test [test file]
//exsample
pub run test test/first_test.dart
2. 한번에 여러 개의 파일 테스트 실행하기
pub run test [dir path]
//example - 테스트 dir내의 모든 테스트 실행
pub run test test
3. 크롬 브라우저에서 실행하기
pub run test -p chrome [file path]
//example - 크롬브라우저에서 test 디렉토리 내의 테스트 모두 실행
pub run test -p chrome test
4. dart vm 실행하기
dart [file path]
//example
dart test/first_test.dart
5. VSC plugin 사용하기
vsc의 Code Runner plugin을 사용하여 단축키로 테스트를 실행 가능합니다. (현재 가장 애용하는 방법입니다)
Ctrl + Alt + N(Window) or ^ + ⌥ + N (Mac) 을눌러 실행합니다.
Ctrl + Alt + M(Window) or ^ + ⌥ + M (Mac) 을눌러 정지합니다.
6. debug F5
7. Run
run을 클릭합니다.
test() , expect() (첫 테스트 작성하기)
void main() {
test('String.split() splits the string on the delimiter', () {
var string = 'foo,bar,baz';
expect(string.split(","), equals(['foo', 'bar', 'baz']));
});
test('String.trim() removes surrounding whitespace', () {
var string = ' foo ';
expect(string.trim(), equals('foo'));
});
}
test()를 이용하여 테스트를 작성합니다
첫 번째 인자로 description을 두 번째 인자로 body()를 전달받습니다.
body안에서 expect assertion을 이용하여 검증합니다.
아래는 test 함수의 매개변수들입니다 . 여러 인자로 옵션을 줄 수 있습니다.
@isTest
void test(description, body(),
{String testOn,
Timeout timeout,
skip,
tags,
Map<String, dynamic> onPlatform,
int retry,
@deprecated bool solo = false}) {
_declarer.test(description.toString(), body,
testOn: testOn,
timeout: timeout,
skip: skip,
onPlatform: onPlatform,
tags: tags,
retry: retry,
solo: solo);
// Force dart2js not to inline this function. We need it to be separate from
// `main()` in JS stack traces in order to properly determine the line and
// column where the test was defined. See sdk#26705.
return;
return; // ignore: dead_code
}
group
void main() {
group('String', () {
test('.split() splits the string on the delimiter', () {
var string = 'foo,bar,baz';
expect(string.split(','), equals(['foo', 'bar', "baz"]));
});
test('.trim() removes surrounding whitespace', () {
var string = ' foo ';
expect(string.trim(), equals('foo'));
});
});
group('int', () {
test('.remainder() returns the remainder of division', () {
expect(11.remainder(3), equals(2));
});
test('.toRadixString() returns a hex string', () {
expect(11.toRadixString(16), equals('b'));
});
});
}
group을 이용하여 테스트를 그룹화 할 수 있습니다.
그룹마다 description을 추가할 수 있습니다.
matcher
void main() {
test('.split() splits the string on the delimiter', () {
expect('foo,bar,baz', allOf([
contains('foo'),
isNot(startsWith('bar')),
endsWith('baz')
]));
});
}
matcher 패키지늬 matcher를 사용하여 복잡한 검증도 수행할 수 있습니다.
https://pub.dev/documentation/matcher/latest/matcher/matcher-library.html
setUp() ,tearDown()
setUp과 tearDown을 이용하여 테스트 간에 코드를 공유할 수 있습니다.
setUp()은 모든 테스트 전에 실행됩니다.
tearDown()은 모든 테스트 수행 이후에 실행됩니다.
void main() {
HttpServer httpServer;
Uri url;
setUp(() async {
httpServer = await HttpServer.bind('localhost', 0);
url = Uri.parse('http://${httpServer.address.host}:${httpServer.port}');
});
tearDown(()async{
await httpServer.close(force: true);
httpServer = null;
url = null;
});
/// ....
}
async test (비동기 테스트)
async로 작성된 테스트에서는 await를 사용할 수 있습니다
테스트 러너는 Future 등 비동기 작업이 완료될 때까지 테스트가 완료된 것으로 간주하지 않습니다.
void main() {
test('new Future.value() returns the value', () async {
var value = await Future.value(10);
expect(value, equals(10));
});
}
또한 고급 비 동기화를 위한 유용한 기능과 매칭 기능도 많이 있습니다.
completion()는 Future를 테스트하는 데 사용할 수 있습니다
Future가 완료될 때까지 테스트가 완료되지 않도록 하고, 해당 Future값 에 대해 매처를 실행합니다.
test('new Future.value() returns the value 2222', () {
expect(Future.value(10), completion(equals(10)));
});
throwsA()는 Future에서 특정 유형의 예외가 발생하도록 합니다.(Future Error)
test('new Future.error() throws the error', () {
expect(Future.error('oh no'), throwsA(equals('oh no')));
expect(Future.error(StateError('bad state')), throwsStateError);
});
expectAsync() 함수는 다른 함수를 감싸고 두 개의 작업이 있습니다. 먼저, 랩핑 된 함수를 테스트합니다.
둘째, 함수가 필수 횟수라고 할 때까지 테스트가 완료되지 않도록 합니다.
test('Stream.fromIterable() emits the values in the iterable', () {
var stream = Stream.fromIterable([1, 2, 3]);
stream.listen(expectAsync1((number) {
expect(number, inInclusiveRange(1, 3));
}, count: 3));
});
Stream Matcher (스트림 매쳐)
test패키지는 비동기 스트림을 처리하기 위한 강력한 매처 모음을 제공합니다.
그것들은 표현력 있고 작곡이 가능하며 스트림에서 방출되는 가치에 대한 복잡한 expect()를 쉽게 작성할 수 있습니다.
예를 들면 다음과 같습니다.
void main() {
test('process emits status messages', () {
var stdoutLines = Stream.fromIterable([
'Ready.',
'Loading took 150ms.',
'Succeeded!'
]);
expect(stdoutLines, emitsInOrder([
'Ready.',
startsWith('Loading took'),
emitsAnyOf(['Succeeded!', 'Failed!']),
emitsDone
]));
});
}
Stream에서 방출되는 값들을 순서대로 처리하는 등의 복잡한 expect 등을 쉽게 처리할 수 있습니다.
스트림 매처는 async패키지 StreamQueue클래스 와도 사용할 수 있으며 ,
이를 통해 소비자에게 이벤트를 푸시하지 않고 스트림에서 이벤트를 요청할 수 있습니다.
이 매처는 일치하는 이벤트를 사용하지만 Stream 하나의 구독자만 가질 수 있는 일반과 달리
테스트에서 계속 사용할 수 있도록 나머지 큐를 그대로 둡니다.
예를 들면 다음과 같습니다.
test('process emits a WebSocket URL', () async {
var stdout = StreamQueue(Stream.fromIterable([
'WebSocket URL:',
'ws://localhost:1234/',
'Waiting for connection...'
]));
await expect(stdout, emitsThrough('WebSocket URL:'));
var url = Uri.parse(await stdout.next);
expect(url.host, equals('localhost'));
await expect(stdout, emits('Waiting for connection...'));
});
아래와 같은 내장 스트림 matcher를 사용할 수 있습니다.
다음과 같은 내장 스트림 매처를 사용할 수 있습니다.
- emits() 단일 데이터 이벤트와 일치합니다.
- emitsError() 단일 오류 이벤트와 일치합니다.
- emitsDone 단일 완료 이벤트와 일치합니다.
- mayEmit() 일치하지 않아도 내부 매처와 일치하는 경우 이벤트를 사용합니다.
- mayEmitMultiple()처럼 작동 mayEmit()하지만 가능한 한 여러 번 일치하는 이벤트와 일치합니다.
- emitsAnyOf() 여러 가능한 일치 중 하나 이상과 일치하는 이벤트를 사용합니다.
- emitsInOrder() 여러 개의 매처와 일치하는 이벤트를 연속으로 사용합니다.
- emitsInAnyOrder()처럼 작동 emitsInOrder()하지만 매처를 임의의 순서로 일치시킬 수 있습니다.
- neverEmits()내부 매처와 일치 하지 않고 완료되는 스트림과 일치합니다 .
new StreamMatcher(). 을 호출하여 사용자 정의 스트림 매처를 정의할 수도 있습니다
테스트 구성
skip
테스트 스위트 건너뛰기
@Skip("currently failing (see issue 1234)")
import "package:test/test.dart";
void main() {
// ...
}
group() 건너뛰기, test() 건너뛰기
void main() {
group("complicated algorithm tests", () {
// ...
}, skip: "the algorithm isn't quite right");
test("error-checking test", () {
// ...
}, skip: "TODO: add error-checking.");
}
timeout
기본적으로 테스트는 30 초 동안 활동이 없으면 시간이 초과됩니다.
그러나 이것은 테스트, 그룹 또는 스위트 별로 구성할 수 있습니다.
테스트 스위트의 제한 시간을 변경하려면 @Timeout파일 맨 위에 주석을 추가하십시오.
@Timeout(const Duration(seconds: 45))
import "package:test/test.dart";
void main() {
// ...
}
절대 시간 초과를 설정하는 것 외에도 을 사용하여 시간 초과를 기본값에 상대적으로 설정할 수 있습니다
@Timeout.factor. 예를 들어, @Timeout.factor(1.5)시간 제한을 기본값의 1.5 배로 설정합니다. == 45초
timeout매개 변수를 사용하여 테스트 및 그룹에 대한 시간 종료를 설정할 수 있습니다.
이 매개 변수는 Timeout주석과 같은 개체를 사용합니다. 예를 들면 다음과 같습니다.
void main() {
group("slow tests", () {
// ...
test("even slower test", () {
// ...
}, timeout: new Timeout.factor(2))
}, timeout: new Timeout(new Duration(minutes: 1)));
}
중첩 시간 초과는 가장 바깥쪽에서 가장 안쪽까지 순서대로 적용됩니다.
즉, "더 느린 테스트"는 그룹의 시간제한에 2를 곱하기 때문에 2 분이 걸립니다.