TDD는 Test-Driven-Development 의 약자로 테스트 주도 개발이라고 한다.
TDD는 불확실성이 높을 때 '피드백'과 '협력'이 중요하다는 이유로 테스트 주도 개발을 하면 더 좋은 결과가 나올 수 있다.
"test"가 명사가 된다면 공유가 쉬워진다. 다른 사람의 코드를 쉽게 접근가능하게하고, 이해가 빨라진다.
그러므로, 다른 사람의 코드 의도를 확인할 수 있게 된다.
튼튼한 객체 지향적인 코드 생산 : TDD는 코드의 재사용 보장을 명시하므로 TDD를 통한 소프트웨어 개발 시 기능 별 철저한 모듈화가 이뤄진다.
재설계 시간의 단축 : 테스트 시나리오를 작성하면서 다양한 예외를 생각하게 되는데 이는 개발 진행 중 소프트웨어의 전반적인 설계가 변경되는 일을 방지할 수 있다.
디버깅 시간의 단축 : 어디서 발생하는 오류인지 유닛 테스트를 통해 특정 오류를 쉽게 찾아낼 수 있다.
추가 구현의 용이함 : TDD의 경우 자동화된 유닛 테스팅을 전제하므로 테스트 기간을 획기적으로 단축시킬 수 있다.
1,2을 ,로 split() 했을 때 1과 2로 잘 분리되는지 확인하는 학습 테스트를 구현한다.
1을 ,로 split() 했을 때 1만을 포함하는 배열이 반환되는지에 대한 학습 테스트를 구현한다.
@Test
@DisplayName("요구사항 1")
void split() {
String str1 = "1,2";
String[] arrStr1 = str1.split(",");
assertThat(arrStr1).contains("1","2");
assertThat(arrStr1).containsExactly("1","2");
}
순서와 상관 없이 실제 그룹이 주어진 값들을 포함하는지를 테스트하는 메서드
순서를 고려해 실제 그룹지 주어진 값을 포함하는지를 테스트하는 메서드
(1,2) 값이 주어졌을 때 String의 substring() 메소드를 활용해 ()을 제거하고 1,2를 반환하도록 구현한다.
@Test
@DisplayName("요구사항 2")
void subString() {
String str = "(1,2)";
String subStringStr = str.substring(1,4);
assertThat(subStringStr).isEqualTo("1,2");
}
str.substring( start, end );
위와 같이 사용할 수 있으며, start와 end를 지정할 수 있다.
위 코드는 1과 4를 시작과 끝으로 지정해서 1,2가 나오는 것이다.
"abc" 값이 주어졌을 때 String의 charAt() 메소드를 활용해 특정 위치의 문자를 가져오는 학습 테스트를 구현한다.
@Test
@DisplayName("요구사항 3")
void charAt() {
String str = "abc";
char c = str.charAt(2);
assertThat(c).isEqualTo('c');
}
charAt()은 String으로 저장된 문자열 중에서 한 글자만 선택해서 char타입으로 변환해준다.
String의 charAt() 메소드를 활용해 특정 위치의 문자를 가져올 때 위치 값을 벗어나면 StringIndexOutOfBoundsException이 발생하는 부분에 대한 학습 테스트를 구현한다.
@Test
@DisplayName("요구사항 3 - 2 : assertThatThrownBy 를 사용한 Exception 처리")
void StringIndexAtException() {
String str = "abc";
assertThatThrownBy(() -> {
char c = str.charAt(5);
assertThat(c).isEqualTo('c');
}).isInstanceOf(StringIndexOutOfBoundsException.class)
.hasMessageContaining("범위를 벗어난 오류");
}
자주 발생하는 Exception의 경우 Exception별 메서드 제공하고 있음
자바 8 예외 assertion 표준 스타일이다.
예외를 처리하기 위한 방법 중 하나로 try~catch문으로 예외를 던졌을 때 테스트가 가능해진다.
@Test
@DisplayName("요구사항 3 - 3 : assertThatExceptionOfType 을 사용한 Exception 처리")
void StringIndexAtException2() {
String str = "abc";
assertThatExceptionOfType(StringIndexOutOfBoundsException.class)
.isThrownBy(() -> {
char c = str.charAt(5);
assertThat(c).isEqualTo('c');
}).withMessageContaining("범위를 벗어난 오류");
}
예외 클래스로 넘기는 방식으로 이 또한 assertThatThrownBy와 함께 예외를 처리하는 방법 중 하나이다.
Set의 size() 메소드를 활용해 Set의 크기를 확인하는 학습테스트를 구현한다.
@Test
@DisplayName("Set Size 구하기")
void getSize() {
int size = numbers.size();
// Set은 중복을 허용하지 않음
assertThat(size).isEqualTo(3);
}
Set은 중복을 허용하지 않아서 1,1,2,3을 add해도 1,2,3만 add된다.
Set의 contains() 메소드를 활용해 1, 2, 3의 값이 존재하는지를 확인하는 학습테스트를 구현하려한다.
JUnit의 ParameterizedTest를 활용해 중복 코드를 제거해 본다.
@ParameterizedTest
@DisplayName("요구사항 2 : Set 내부 값의 존재 여부 확인")
@ValueSource(ints = {1,2,3})
void useValueSource(int nums) {
assertTrue(numbers.contains(nums));
}
assertTrue, assertFalse 메서드는 boolean 값을 반환하는 메서드를 테스트하기에 적합하다.
요구사항 2는 contains 메소드 결과 값이 true인 경우만 테스트 가능하다.
입력 값에 따라 결과 값이 다른 경우에 대한 테스트도 가능하도록 구현한다.
예를 들어 1, 2, 3 값은 contains 메소드 실행결과 true, 4, 5 값을 넣으면 false 가 반환되는 테스트를 하나의 Test Case로 구현한다.
@ParameterizedTest
@DisplayName("요구사항 3 : CsvSource 사용하여 boolean 반환")
@CsvSource(value = {"1:true","2:true","3:true","4:false","5:false"},delimiter = ':')
void useCsvSource(int element, boolean expected) {
assertThat(numbers.contains(element)).isEqualTo(expected);
assertEquals(numbers.contains(element), expected);
}