우아한테크코스 1주차를 진행하면서 새롭게 알게된 내용 혹은 알고 있다고 생각했지만 잘 모르고 있던 내용을 위주로 공부한 것을 정리한다는 느낌에서 작성해보았습니다!
assertThat()과 함께 사용하는 contains() 메소드는 Verifies that the actual array contains the given values, in any order.
과 같은 기능을 제공한다고 설명하고 있 다. 즉, 어떤 순서로든 (순서에 관계없이) 배열에 지정된 값이 포함되어 있는지 여부를 확인시켜준다!
JUnit을 학습하면서는 다음과 같은 코드에서 활용해보았다.
assertThat(split("1,2")).contains("1", "2");
...
private String[] split(String input) {
return input.split(",");
}
단순하게 "1,2" 와 같은 문자열을 입력받으면 ","를 구분자로 하여 분리한 String[]을 반환해주는 split()함수를 호출하는 것이다. 그리고 그 결과 배열에 "1", "2"가 포함되었는지를 확인하는데서 활용해보았다.
추가적으로 발생가능한 예외는 다음과 같다.
Throws:
NullPointerException – if the given argument is null.
IllegalArgumentException – if the given argument is an empty array.
AssertionError – if the actual array is null.
AssertionError – if the actual array does not contain the given values.
containsExactly() 메소드는 Verifies that the actual array contains exactly the given values and nothing else, in order.
라고 설명되어 있으며 이를 해석하여 보면 검사하려는 배열에 정확히 주어진 값이 포함되어 있고, 앞서 contains()에서 순서를 고려하지 않았다면 순서까지 올바른지를 확인한다.
이 때 원소 하나라도 빠트리거나 순서가 잘못되면 테스트가 통과하지 못하므로 정확하게 일치하는 배열 혹은 리스트여야만 통과하게 된다!
예제 코드를 보며 다시 한 번 익혀보자!
List<String> stringList = Arrays.asList("a","b","c");
// 통과!
assertThat(stringList).containsExactly("a",b","c");
//실패!
assertThat(stringList).containsExactly("b",a","c");
//실패!
assertThat(stringList).containsExactly("a",b",);
위 두 메소드는 평소에 JUnit과 친하지는 않았지만 사용할 일이 있게되면 종종 사용하곤 했던 메소드이다. 이번에 이렇게 다시 한 번 정리하는 이유는 리스트의 순서에 관한 사항에 대해서는 고려해보지 않았었고 새롭게 알게 된 사실이기 때문이다!
마찬가지로 isInstanceOf() 메소드도 docs를 참고해보면 Verifies that the actual value is an instance of the given type.
라고 설명이 적혀있다. 즉, 실제 값이 지정된 유형의 인스턴스인지 확인한다는 의미이다.
다음과 같은 코드를 통해서 해당 메소드를 접하고 학습해보았다.
@Test
@DisplayName("인덱스 범위 테스트")
public void 특정_위치_문자_테스트() {
assertThatThrownBy(()-> "abc".charAt(3))
.isInstanceOf(StringIndexOutOfBoundsException.class);
}
여기서는 "abc"는 인덱스가 0~2이기 때문에 charAt()을 통해서 인덱스가 3인 문자(char)에 대한 접근시 IndexOutOfBoundsException
이라는 객체가 생성되어 예외가 던져지게 될 것이다. 따라서 해당 객체가 위의 설명과 같이 해당 유형의 객체인지를 확인하는 의미에서 isInstanceOf()를 활용해보았다.
DisplayName 어노테이션은 테스트 클래스 혹은 테스트 메소드에 대한 사용자 정의 표시 이름을 선언하는데 사용된다. 따라서 해당 단위 테스트 메소드가 어떤 테스트를 하는지를 보다 명확하게 (한글로..) 표현해줄 수 있다.
이와 관련해서 @SuppressWarnings("NonAsciiCharacters")
를 알게 되었다. 우리가 흔히 테스트 코드를 작성할 때는 단위 테스트 메소드명을 한글로 짓고는 한다.
@Test
public void 숫자_한개_테스트() {
assertThat(split("1")).containsExactly("1");
}
하지만 한글은 아스키 코드에 포함되지 않으므로 이렇게 되면 IDE 등에서 경고를 발생시킬 수 있는데 이를 방지하고자 위 어노테이션을 사용해준다고 한다.
따라서 @SuppressWarnings("NonAsciiCharacters")
와 함께 @DisplayName
을 사용해주면 테스트 코드 자체가 하나의 읽기 좋은 문서가 되는데 이바지할 수 있다고 생각한다!
단순히 매개변수에만 차이가 있고, 동일한 테스트를 한다고 하면 ParameterizedTest 어노테이션이 굉장히 유요할 수 있다! 하지만 이 자체로 기능을 가지는 것은 아니고 @ParameterizedTest is used to signal that the annotated method is a parameterized test method.
에서 알 수 있듯이 해당 테스트가 매개 변수화된 테스트 메소드임을 알리는데 사용된다. 따라서 아래 두 가지 어노테이션과 함께 @ParameterizedTest
를 사용할 수 있다.
@ParameterizedTest methods must specify at least one ArgumentsProvider via @ArgumentsSource or a corresponding composed annotation (e.g., @ValueSource, @CsvSource, etc.).
해당 설명에서도 알 수 있다시피 @ParameterizedTest 단독으로 쓰인다기 보다는 위와 같은 어노테이션과 함께 지정되어 사용되어야한다는 것을 알 수 있습니다!
ValueSource 어노테이션은 리터럴 값의 단일 배열을 지정할 수 있으며 매개 변수화된 테스트 호출마다 단일 인수를 제공합니다.
제공되는 리터럴 값 : short, byte, int, long, float, double, char, boolean, String, Class
예시 코드
@ValueSource로 주어진 int형의 매개변수 단일 배열 1,2,3에 대해서 테스트 코드가 각각 돌아가게 됩니다.
@ParameterizedTest
@ValueSource(ints = {1,2,3})
public void 집합_원소_존재_테스트(int number) {
assertTrue(numbers.contains(number));
}
@CsvSource is an ArgumentsSource which reads comma-separated values (CSV) from one or more CSV records supplied via the value attribute or textBlock attribute.
CsvSource 어노테이션은 값 속성 또는 textBlock 속성을 통해 제공된 하나 이상의 CSV 레코드에서 CSV(쉼표로 구분된 값)을 읽는 인수 소스입니다. 라고 해석된다. 즉 CSV 레코드를 delimiter와 함께 주어 위의 ValueSource와 같이 파라미터를 줄 수 있다.
(콤마(default)를 기준으로 CSV를 구분하지만 아래 예시코드와 같이 커스터마이징이 가능하다.)
예시 코드
@ParameterizedTest
@CsvSource(value = {"1:true", "2:true", "3:true", "4:false", "5:false"}, delimiter = ':')
public void 집합_원소_존재_테스트_2(int number, boolean expected) {
assertEquals(numbers.contains(number), expected);
}
학습 목표
프로그래밍 요구사항
구현 저장소
자동차 경주 구현과 같이 프로젝트에 구현하였습니다.
문자열 덧셈 계산기 구현 저장소
학습 내용
학습 목표에서 언급된 것과 같이 이번 문자열 덧셈 계산기
를 구현하면서 크게 2가지를 얻으려고 했다. 첫번째로는 TDD 에 따라서 개발을 해보는 것이었고, 두번째로는 처음 접해보는 페어 프로그래밍
을 통해서 과제를 수행하는 것이었다!
우선 TDD란 Test Driven Development의 약자로 '테스트 주도 개발'이라고 해석된다. 즉, 테스트가 주가되는 개발이라고 이야기할 수 있다.
내가 이해한 TDD의 사이클은 우선 가장 작은 실패하는 테스트 코드
를 먼저 작성한다. 이후 해당 테스트 코드를 통과하도록 하는 가장 작은 프로덕션 코드를 작성한다. 이후 중복 코드 제거 등의 리팩토링을 수행하는 식이다.
본인이 TDD를 하면서 가장 어려웠던 점은 문자열 덧셈 피드백 수업
을 통해서 알 수 있었는데, 실패하도록 작성된 테스트를 통과하는 최소한의 프로덕션 코드의 기준이었다. 예를 들어서 다음과 같은 테스트 코드를 작성했다고 하면..
@Test
public void splitAndSum_null() {
int result = Calculator.splitAndSum(null);
assertThat(result).isEqualTo(0);
}
이를 통과하는 최소한의 코드는 다음과 같다.
public static int splitAndSum(String str) {
return 0;
}
하지만 본인은 이를 통과하는 최소한의 코드가 아닌 다음과 같은 형태로 작성하였었다.
public static int splitAndSum(String str) {
if (str == null) {
return 0;
}
}
이러한 부분에서 TDD를 사용하면서 아직은 최소한의 단위에 대한 정의가 스스로 되어있지 않다고 느꼈고, 많은 TDD 연습을 통해 개선해야할 부분이라고 느꼈다.
뿐만 아니라 문자열 덧셈 계산기
라는 작은 프로그램 TDD로 개발하니 테스트 코드 먼저 생각하고 이후 구현을 생각하게 되어 어려운 부분이 많았다. 미션을 수행하면서 틈틈이 TDD로 구현해보려고 노력해봐야겠다. (적어도 설계 단계에서..머릿속으로 생각이라도 TDD 흐름으로 생각해보아야겠다.)
페어 프로그래밍이란 하나의 커뮤터에서 두 사람 이상의 개발자가 작업을 하는 방법
을 이야기한다. 우테코에서는 이런 페어 프로그래밍을 적극 권장하고 있다. 그 이유에는 첫번째로 즉각적인 피드백을 서로에게 줄 수 있다는 점과 두번째로 해당 코드에 대한 책임을 혼자 지는 것이 아닌 둘 혹은 그 이상이상 진다는 점에 있으며 또한 서로간의 갈등이 바로바로 드러나게 함으로써 나중에 커질 문제를 미리미리 겪게 된다는 장점이 있기 때문이다.
직접 문자열 덧셈 계산기
뿐만 아니라 자동차 경주 게임
을 페어 프로그래밍으로 진행하면서 느낀 가장 큰 장점은 동일한 문제에 대한 상대방의 의견 혹은 생각을 즉시 들어볼 수 있고, 이에 대한 나의 생각 그리고 상대방의 생각을 나누며 보다 나은 새로운 아이디어가 떠오를 수 있다는 경험이었다. 그리고 본인처럼 소심한 사람도 단 둘이서 혹은 3명이서 진행하다 보니 그나마 자신의 의견을 이야기하는데 있어 주저함이나 부담감을 덜 수 있다는 점 또한 개인적으로 페어 프로그래밍의 매력 중 하나라는 생각이 들었다.
이것으로 우아한 테크 코스 1주차 학습 내용 + 강의 내용에 대한 정리를 마무리하도록 하겠습니다. 아무래도 블로그 글 쓰기가 습관이 되어있지 않다보니 그 때 당시 들었던 생각들을 기록하지 못하고 빠트린 부분도 더러 있다고 생각됩니다. 향후 글들에서는 꾸준함을 유지하여 내가 들었던 생각이나 학습 내용들을 빠트리지 않고 모두 기록할 수 있는 블로거로 성장해 보겠습니다!!