Setup
실질적으로 테스트를 할 객체를 생성한다.
main() {
test('The calculator returns 8 when adding 6 and 2', () {
// 1st step: setup -> create the calculator object
final calculator= Calculator();
});
}
Side Effects
실제로 테스트를 진행할 함수 등 사이드이펙트를 낳을 수 있는 요소를 생성한다.
main() {
test('The calculator returns 8 when adding 6 and 2', () {
// 1st step: setup -> create the calculator object
final calculator= Calculator();
// 2nd step: side effect -> collect the result you want to test
final result = calculator.add(6,2);
});
}
Expectation
요소를 실행하고 기댓값과 비교 절차를 수행한다.
main() {
test('The calculator returns 8 when adding 6 and 2', () {
// 1st step: setup -> create the calculator object
final calculator= Calculator();
// 2nd step: side effect -> collect the result you want to test
final result = calculator.add(6,2);
// 3rd step: expectation -> compare the result against and expected value
expect(result,8);
});
}
이같은 요소는 가독성과 유지보수성을 위해 따라야 하는 중요한 단계다.
test()
첫번째 파라미터는 description 파라미터로 테스트의 목표에 대한 설명을 넣는 부분이다.
두번째 파라미터는 function 파라미터로 테스트 하고자 하는 로직을 넣고 예상치와 비교를 실행하는 부분이다.
다음과 같이 Calculator 클래스가 있다고 하자
class Calculator{
int add(int a, int b) {
return a + b;
}
int subtract(int a, int b) {
return a - b;
}
int multiply(int a, int b) {
return a * b;
}
double divide(double a, double b) {
return a / b;
}
}
이 클래스의 메서드들은 2개의 숫자를 넘겨서 1개의 반환을 돌려받는 형식을 띄고 있다.
이 클래스를 테스트 하려면 먼저 main 메서드와 test function의 설명 부분을 채워준다.
import 'package:test/test.dart';
void main(){
test('The result of addition should be 8 when adding 6 and 2',(){
})
}
그 후 테스트 하고자 하는 클래스의 인스턴스를 생성한다.
import 'package:test/test.dart';
void main(){
test('The result of addition should be 8 when adding 6 and 2',(){
final calculator = Calculator();
})
}
expect()
실질적으로 테스트가 정상적인지에 대한 점검을 수행하는 메서드이다.
첫번째 파라미터로 수행할 함수를 넣어준다. (actual)
두번째 파라미터로 나와야하는 결괏값을 넣어준다. (matcher)
파라미터의 값이 일치하지 않으면 test failure 에러를 던진다.
위에서 작성한 테스트를 실제로 진행한다고 한다면,
main() {
test('The calculator returns 8 when adding 6 and 2', () {
final calculator= Calculator();
expect(calculator.add(6, 2), 8);
});
}
위의 expect 함수는 true 값을 반환할 것이다.
만약 실패할 경우, 세번째 파라미터로 좀 더 자세한 정보를 제공할 수도 있다.
main() {
test('The calculator returns 8 when adding 6 and 2', () {
final calculator= Calculator();
final result = calculator.add(6,2);
expect(result,8.000001, reason: 'It should be exactly 8');
});
}
group()
예를 들어 다음과 같은 코드가 있다고 하자.
main() {
test('The calculator returns 8 when adding 6 and 2', () {
final calculator = Calculator();
expect(calculator.add(6, 2), 8);
});
test('The calculator returns 4 when subtracting 2 from 6', () {
final calculator = Calculator();
expect(calculator.subtract(6, 2), 4);
});
test('The calculator returns 8 when multiplying 4 with 2', () {
final calculator = Calculator();
expect(calculator.multiply(4, 2), 8);
});
test('The calculator returns 9 when diving 18 and 2', () {
final calculator = Calculator();
expect(calculator.divide(18, 2), 9);
});
}
이 경우는 각 연산 종류마다 한 개의 테스트만 존재하지만, 만약 각 종류들이 각자 다른 갯수로 여러개가 존재한다면 동일한 깊이이기 때문에 결과를 읽기가 힘들 것이다.
이럴 때 group()
함수를 통해 깊이를 추가해줄 수 있다.
main() {
group('add', () {
test('The calculator returns 8 when adding 6 and 2', () {
final calculator = Calculator();
expect(calculator.add(6, 2), 8);
});
});
group('subtract', () {
test('The calculator returns 4 when subtracting 2 from 6', () {
final calculator = Calculator();
expect(calculator.subtract(6, 2), 4);
});
});
group('multiply', () {
test('The calculator returns 8 when multiplying 4 with 2', () {
final calculator = Calculator();
expect(calculator.multiply(4, 2), 8);
});
});
group('divide', () {
test('The calculator returns 9 when diving 18 and 2', () {
final calculator= Calculator();
expect(calculator.divide(18, 2), 9);
});
});
}
Future 테스트
async와 await 키워드를 통해 테스트할 수 있다.
Calculator 클래스에 다음과 같은 코드를 추가했다고 하면,
Future<double>? squareRootOf(double a) =>
Future.delayed(const Duration(seconds: 1), () => math.sqrt(a));
다음과 같이 테스트 할 수 있다.
group('square root', () {
test('The calculator returns 5 when the input is 25', () async {
expect(await calculator.squareRootOf(25), 5);
});
});
Stream 테스트
Calculator 클래스에 다음과 같은 코드를 추가했다고 하면,
Stream<int> fibonacciSequence() => Stream.periodic(
const Duration(seconds: 1), (count) => _fibonacciValues[count]);
final List<int> _fibonacciValues = [0, 1, 1, 2, 3, 5, 8, 13, 21];
expect 함수 내부에 emitInOrder()
matcher 함수를 통해 다음과 같이 테스트 할 수 있다.
group('fibonacci sequence', () {
test('The calculator return [0, 1, 1, 2, 3, 5, 8, 13, 21] in order', () {
expect(calculator.fibonacciSequence(),
emitsInOrder([0, 1, 1, 2, 3, 5, 8, 13, 21]));
});
});