본문 바로가기
교육 및 책/TDD, 클린 코드 with Java

TDD, 클린 코드 학습 테스트

by oneny 2023. 4. 5.

TDD, 클린 코드 with Java 16기 시작

백엔드 개발자가 되자 목표를 하고 2주 동안 열심히 Java를 공부하고 박재성님의 TDD, 클린 코드 강의가 시작되었다! 물론 내가 들을 수준은 아니지만 하나라도 배우자는 마인드를 가지고 열심히 달려보자!

 

1단계 학습 테스트

 

자동차 경주를 시작하기에 앞서 선수 과제가 있었고, 이에 대한 피드백을 정리!

 

String 클래스에 대한 학습 테스트

package study;

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;

public class StringTest {

  @Test
  @DisplayName("(1,2)에서 () 제거 테스트") // () 제거 테스트라고 명시한다면, assertion에서도 제거되었는지 확인
  void substring() {
    String result = "(1,2)".substring(1, 4);
    assertThat(result).isEqualTo("1,2");
  }

  @Test
  @DisplayName("문자열 abc에서 위치값을 벗어나는 경우 StringIndexOutOfBoundsException 발생 테스트")
  void charAtRaiseStringIndexOutOfBoundsException() {
    String abc = "abc";

    // 경계값 테스트의 경우, ParameterizedTest를 적극적으로 활용
    assertThatThrownBy(() -> abc.charAt(abc.length()))
            .isInstanceOf(IndexOutOfBoundsException.class)
            .hasMessageContaining("String index out of range: " + abc.length());
  }
}

 

String 클래스에 대학 학습 테스트 리팩토링

@Test
@DisplayName("(1,2)에서 () 제거 테스트")
void substring() {
  String result = "(1,2)".substring(1, 4);

  assertThat(result)
          .isEqualTo("1,2")
          .doesNotContain("(", ")");
}

doesNoContain 메서드를 활용하여 ()가 제거되었는지 확인할 수 있도록 추가하였다.

 

@ParameterizedTest
@ValueSource(ints = { 4, 6, Integer.MAX_VALUE })
@DisplayName("문자열 abc에서 위치값을 벗어나는 경우 StringIndexOutOfBoundsException 발생 테스트")
void charAtRaiseStringIndexOutOfBoundsException(int index) {
  String abc = "abc";

  assertThatThrownBy(() -> abc.charAt(index))
          .isInstanceOf(IndexOutOfBoundsException.class)
          .hasMessageContaining("String index out of range: " + index);
}

경계값 테스트를 위해 @ParameterizedTest를 활용하여 다음과 같이 리팩토링하였다.

 

Set Collection에 대한 학습 테스트

 

package study;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import org.junit.jupiter.params.provider.ValueSource;

import java.util.HashSet;
import java.util.Set;

import static org.assertj.core.api.Assertions.assertThat;

public class SetTest {
  private Set<Integer> numbers;

  @BeforeEach
  void setUp() {
    numbers = new HashSet<>();
    numbers.add(1);
    numbers.add(1);
    numbers.add(2);
    numbers.add(3);
  }

  @Test
  @DisplayName("Set의 크기 확인 테스트")
  void size() {
    assertThat(numbers.size()).isEqualTo(3); // isEqualTo() 보다는 hasSize() 사용
  }

  @ParameterizedTest
  @ValueSource(ints = {1, 2, 3})
  @DisplayName("Set의 1, 2, 3 값 존재 확인 테스트")
  void contains(int number) {
    assertThat(numbers.contains(number)).isTrue(); // assertion을 활용하여 의도를 분명히 드러내기
  }

  @ParameterizedTest
  @CsvSource(value = {"1:true", "2:true", "3:true", "4:false", "5:false"}, delimiter = ':')
  @DisplayName("Set의 입력값에 따른 결과값 확인 테스트") // 기능 요구사항을 테스트케이스 DisplayName으로 그대로 활용해도 괜찮음
  void contains_true_or_false(int number, boolean expected) {
    assertThat(numbers.contains(number)).isEqualTo(expected);
  }
}

 

Set Collection에 대한 학습 테스트

@Test
@DisplayName("Set의 크기 확인 테스트")
void size() {
  assertThat(numbers).hasSize(3);
}

assertThat(numbers.size()).isEqualTo(3); 보다는 assertThat(numbers).hasSize(3);로 내가 뭘 테스트하는지 좀 더 명시적이고 선언적으로 리팩토링했다.

 

@Test
@DisplayName("Set의 1, 2, 3 값 존재 확인 테스트")
void contains() {
  assertThat(numbers).contains(1, 2, 3);
}

assertion을 활용하여 의도를 좀 더 분명히 나타내어 위처럼 명시적으로 리팩토링하였다.

 

@ParameterizedTest
@CsvSource(value = {"1:true", "2:true", "3:true", "4:false", "5:false"}, delimiter = ':')
@DisplayName("입력 값에 따라 결과 값이 다른 경우에 대한 테스트")
void contains_true_or_false(int number, boolean expected) {
  assertThat(numbers.contains(number)).isEqualTo(expected);
}

DisplayName에 대해서도 피드백을 받았는데 이 부분이 제일 깜짝 놀랐다.

피드백 주신 분도 실제 협업에서 기능요구 사항을 도출한 후 TODO로 작성하고, 이를 테스트 케이스로 활용한다고 말씀하셨고, 이를 바탕으로 Set Collection에 대한 학습 테스트에 나와있는 요구사항에 따라 DisplayName을 활용하였다.