: 개별 함수, 메소드, 클레스를 테스트 할 때 사용
Repository, data model, service, controller 각각을 별개로 테스팅할 때 유용함
정상적으로 작동한다면 빠르게 테스트를 진행할 수 있음
: 개별 위젯을 테스트할 때 사용
실기기 또는 애뮬레이터에서 실행하지 않기 때문에 빠르다는 특성을 가지고 있음
: 앱 전체 또는 앱에서 비중을 많이 차지하는 부분을 테스트 할 때 사용
실기기 또는 시뮬레이터에서 실행되는 테스트이기에 테스트 시작 / 완료가 느림
client 측에서만 테스트 가능
: 프론트엔드, 백엔드 모두 테스트 가능, 주로 시스템 자체가 의도대로 동작하는지 테스트할 때 사용
네트워크 연결 문제 등 외부 요소에 영향을 받는 경우가 있기에 세팅 및 유지보수가 어려움.
특정 Repository의 인스턴스를 정의
final respotiroy = Repository();
expect()
함수 추가
expect(): 2개 이상의 인자를 가짐
expect(repository.get(), kTestValue);
TIP!
toString()
메소드를 사용하면 instance of ___
문구 대신 정확하게 어떤 값이 들어갔는지 확인 가능equality operator
를 사용하면 해시코드까지 비교하여 해당 인스턴스가 정확하게 같은 값인지 확인 가능test()
메소드 추가
아래와 같이 프로퍼티 추가
test('get(1) returns first item', () {
final repository = Repository();
expect(
productsRepository.get('1'),
kTestArray[0],
);
});
만약 특정 데이터가 null인지 확인하고 싶으면, Property를 null로 변경 후 체크
test('get(1) returns first item', () {
final repository = Repository();
expect(
productsRepository.get('1'),
null,
);
});
exception을 처리하고 싶은데 null 데이터가 오는 경우에는 어떻게 처리해야하는가?
방법 1) Closure-based Syntax (throws error)
test('get(100) returns first item', () {
final repository = Repository();
expect(
() => repository.get('100'), // here
noContentError, // repository.get('100')의 값이 NULL이라면, noContentError 호출
);
});
}
방법 2) No Closure (returns value)
Product? get(String id) {
try {
return _kTestArray.firstWhere((product) => product.id == id);
} catch (e) {
return null;
}
}
-------
test('get(100) returns first item', () {
final repository = Repository();
expect(
repository.get('100'), // here
null,
);
});
}
group()
메소드를 사용하여 여러 테스트를 한꺼번에 실행할 수 있음
group('Repository', () {
test(...);
test(...);
})
: Async / Await 사용
test("___", () async {
final_
expect(
await _.fetchProductsList(),
testData
);
});
: emit()
와 같은 Stream Matcher 사용
test("___", () {
final _;
expect(
_.fetchProductsList(),
emits(testData); // emitting a value
);
});
emitsInOrder([])
: stream에 추가되는 여러 값들을 체크test('currentUser is null after sign out', () async {
final authRepository = makeAuthRepository();
await authRepository.signInWithEmailAndPassword(testEmail, testPassword);
expect(
authRepository.authStateChanges(),
// emitsInOrder: to check the various values that are added to the stream
emitsInOrder([
testUser, // latest value from signInWithEmailAndPassword()
null, // upcoming value from signOut()
]));
await authRepository.signOut();
expect(authRepository.currentUser, null);
});
test('currentUser is null after sign out', () async {
final authRepository = makeAuthRepository();
addTearDown(authRepository.dispose);
await authRepository.signInWithEmailAndPassword(
testEmail,
testPassword,
);
expect(authRepository.currentUser, testUser);
expect(authRepository.authStateChanges(), emits(testUser));
await authRepository.signOut(); // check one more tie after signing out
expect(authRepository.currentUser, null);
expect(authRepository.authStateChanges(), emits(null));
});
verify(authRepository.signOut).called(1);
expect(controller.debugState, const AsyncData<void>(null));
: 리턴되는 값의 타입을 체크하는 메소드
예시
expect(controller.debugState, isA<AsyncError>());
expectLater(
controller.stream,
emitsInOrder(const [
AsyncLoading<void>(),
AsyncData<void>(null),
])
);
stack trace를 신경쓰지 않고 값 테스트 가능.
예시
expectLater(
controller.stream,
emitsInOrder(const [
AsyncLoading<void>(),
predicate<AsyncValue<void>>((value) {
expect(value.hasError, true);
return true;
}),
])
);
setUpAll()
: 테스트가 실행되기 직전 단 한번만 호출되는 메소드setUp()
: 각 테스트가 실행되기 전 실행. 변수 초기화를 주로 진행tearDownAll()
: 테스트가 끝난 직후 단 한번만 호출되는 메소드test('currentUser is not null after sign in', () async {
final authRepository = makeAuthRepository();
await authRepository.signInWithEmailAndPassword(
testEmail,
testPassword,
);
expect(authRepository.currentUser, testUser);
expect(authRepository.authStateChanges(), emits(testUser));
authRepository.dispose(); // cleanup
});
위와 같이 처리하게 되면 테스트가 실패했을 때 repository가 정상적으로 dispose되지 않음
⇒ addTearDown()
사용
test('currentUser is not null after sign in', () async {
final authRepository = makeAuthRepository();
addTearDown(authRepository.dispose);
await authRepository.signInWithEmailAndPassword(
testEmail,
testPassword,
);
expect(authRepository.currentUser, testUser);
expect(authRepository.authStateChanges(), emits(testUser));
});
group('submit', () {
test('''
Given) formType이 signIn 인 경우
When) signInWithEmailAndPassword가 성공하면
Then) true 반환 후, state를 Asyncs가 되어야함
''', () { ... });
});
expect(
controller.debugState,
// using predicate since we can't match the stack trace
predicate<EmailPasswordSignInState>((state) {
expect(state.formType, EmailPasswordSignInFormType.signIn);
expect(state.value.hasError, true);
return true;
}),
);