
: 개별 함수, 메소드, 클레스를 테스트 할 때 사용
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;
}),
);