본문 바로가기
개인공부/테스트 코드 ( Unit Test )

Unit Test Code ( 단위 테스트 코드)

by 응가1414 2023. 11. 3.

 

테스트 코드를 사용을 하는 이유는 저의 생각에는....

코드의 메서드를 작성하면서 클래스 간의 종속성을 고려하는 시간은 매우 중요합니다. 테스트 코드를 작성하면서 각 클래스의 종속성과 독립성을 고려하는 것은 큰 의미가 있다고 생각합니다.

 

또한, 메서드에 두 가지 이상의 책임을 할당할 때는 테스트 코드 작성이 어려워 메서드를 분리하는 방향을 잡기가 편했습니다.

 

테스트 코드를 작성한 메서드와 그렇지 않은 메서드를 비교했을 때 차이를 느끼는 일이 자주 있습니다.

스트 코드를 작성하지 않은 코드는 불확실성이 생겨 다음 작업을 진행하기가 어려웠습니다. 그러나 테스트 코드를 작성한 메서드는 안심하고 다음 작업을 진행할 수 있게 해줍니다.

 

테스트 코드를 믿음만으로만 신뢰하는 것보다는 예외 상황에 대비하여 안전장치를 항상 고려하는 것이 중요합니다.

 

학습 테스트 진행 방식

  • import한 저장소의 src/test/java 학습 테스트를 위한 패키지를 생성한다.
    • 예를 들어 study
  • 단위 테스트를 위한 클래스 명명 규칙("테스트 대상 클래스명 + Test")에 따라 클래스를 생성한 다.
  • 요구 상항 또는 기능목록표에 관한 모든 예외적인 부분을 체크한다.
  • Assertions Reference을 보며 공부, 및 정리

Assertions의 기본문법

주장 에는 다양한 종류가 있습니다 contains. 가장 관련성이 높은 것을 선택하는 데 도움이 되는 표는 다음과 같습니다.

역설설명

contains 실제 반복 가능/배열에 주어진 값이 어떤 순서로든 포함되어 있는지 확인합니다.
containsOnly 실제 그룹에 지정된 값만 포함되어 있고 순서에 관계없이 다른 항목이 포함되지 않았는지 확인하고 중복 항목을 무시합니다(즉, 값이 발견되면 해당 중복 항목도 발견된 것으로 간주됩니다).
containsExactly 실제 반복 가능/배열에 주어진 값이 정확히 포함되어 있고 순서대로 다른 항목은 포함되어 있지 않은지 확인합니다.
containsExactlyInAnyOrder 실제 반복 가능/배열에 지정된 값이 정확히 포함되어 있고 순서에 관계 없이 다른 항목이 포함되어 있지 않은지 확인합니다.
containsSequence 실제 그룹에 지정된 시퀀스가 올바른 순서로 포함되어 있고 시퀀스 값 사이에 추가 값이 없는지 확인합니다.
containsSubsequence 실제 그룹에 주어진 하위 시퀀스가 올바른 순서로 포함되어 있고 그 사이에 다른 값이 있는지 확인합니다.
containsOnlyOnce 실제 반복 가능/배열에 주어진 값이 한 번만 포함되어 있는지 확인합니다.
containsAnyOf 실제 반복 가능/배열에 주어진 값 중 적어도 하나가 포함되어 있는지 확인합니다( or주어진 값에 대한 연산자와 유사).
extracting 각 요소에서 추출(또는 전달)할 필드/속성을 지정 Function하고 추출된 값에 대해 어설션을 수행합니다.
extracting().contains(tuple) 여러 값 추출, 테스트 중인 요소에서 여러 값을 추출하고 투플을 사용하여 확인가능하다.
flatExtracting("리스트이름") .contains(); 리스트의 원소값인 클래스안의 리스트의 값을 추출한다.

---- Parameter 매개변수 테스트

0. CsvSource을 이용한 각각의 매개면수에 원하는 값 넣기

     @DisplayName("유저의 입력한 숫자중 특정한 index의 값의 숫자와 컴퓨터의 저장된 3개의 숫자중 특정 index에 숫자가 같은지 확인한다.")
     @ParameterizedTest
     @CsvSource({"0,1,true", "1,2,true", "2,3,true", "0,4,false", "1,4,false", "2,4,false"})
     public void isSamePlaceNumUserAndComputerTest(String placeIndexOfUser, int numIndexPlaceOfUser, boolean expectResult) throws Exception {
 ​
     }
 }

1. String 변수 받는법

 @ParameterizedTest(name = "매개변수") // 무조건 name으로
 @ValueSource(strings = {"q", "qwerasdfzxcv", "qq23"})
 @DisplayName("String 변수 받는법")
 void 매개변수를_테스트하는법(String name){
   assertThat(name).contains("1");
 }

---- null , empty 변수에 넣는법

1. empty 빈것을 변수 넣는법

 @ParameterizedTest
 @NullSource
 @EmptySource
 void nullEmptyStrings(String text) {
     assertTrue(text == null || text.trim().isEmpty());
 }

2. null 과 empty 를 넣는다.

 @ParameterizedTest
 @NullAndEmptySource
 @ValueSource(strings = { " ", "   ", "\t", "\n" })
 void nullEmptyAndBlankStrings(String text) {
     assertTrue(text == null || text.trim().isEmpty());
 }

3. null, empty, 매개변수

 @ParameterizedTest
 @NullSource
 @EmptySource
 @ValueSource(strings = {"1", "13"})
 void nullEmptyStrings(String text) {
     assertTrue(text == null || text.trim().isEmpty() 
                || Integer.parseInt(text) > 0 && Integer.parseInt(text) < 4);
 }

---- Exception_예외 테스트

1. assertThrows

 import static org.junit.jupiter.api.Assertions.assertThrows;
 ​
 @Test
 void 예외를_던지는_테스트() {
   // given
   String input = "pobi,crong,honuxxx";
   // when then
   assertThrows(IllegalArgumentException.class, (원소의값) -> 메소드콜());
   
  assertThrows(IllegalArgumentException.class, user::getInputAllRightNumOfUser);
  assertThrows(IllegalArgumentException.class, Class::메서드명);
 }

2. assertThatThrownBy

 @Test
 void charAt_메서드_사용시_문자열의_길이보다_큰_숫자_위치의_문자를_찾을_때_예외_발생() {
   String input = "abc";
 ​
    assertThatThrownBy(() -> input.charAt(5))
       .isInstanceOf(StringIndexOutOfBoundsException.class)
       .hasMessageContaining("String index out of range: 5");
 }
 ​
 @Test
 public void exception_assertion_example() {
     assertThatThrownBy(() -> { throw new Exception("boom!");        }).isInstanceOf(Exception.class)
        .hasMessageContaining("boom");
 };
 ​
 // #2
         Assertions.assertThatThrownBy(
                         () -> ValidException.isValidFIveLessString(input))
                 .isInstanceOf(IllegalArgumentException.class);

3. assertThatExceptionOfType

예외가 발생하는지 확인

     @Test
     public void testException() {
         assertThatExceptionOfType(IOException.class).isThrownBy(() -> { throw new IOException("boom!"); })
                 .withMessage("%s!", "boom")
                 .withMessageContaining("boom")
                 .withNoCause();
     }

---- 입력(in), 출력(out) 테스트

###

 ## 출력(out) 테스트
   ...
   private ByteArrayOutputStream outContent;
 ​
     @BeforeEach
     void setUp() {
         outContent = new ByteArrayOutputStream();
         System.setOut(new PrintStream(outContent));
     }
   ...
 ​
     @DisplayName("print new Line 테스트")
     @Test
     void printNewLine() {
         // when
         Utill.printNewLine();
 ​
         // than
         assertThat(outContent.toString()).isEqualTo("\n");
     }
 ## 입력 테스트_1
     @Test
     void 입력_출력값을_테스트() {
 ​
        // 입력을 담는다.
        String input = "입력하고싶은_문자열";
        InputStream in = new ByteArrayInputStream(input.getBytes());
        System.setIn(in);
   
        // when 
        calculator.start();
   
        // then
        Assertions.assertThat(errMsg).isEqualTo(out.toString());
     }
 }
 ## MockedStatic 을 이용해 입력 테스트_2
   
 import static org.mockito.BDDMockito.given;
 import static org.mockito.BDDMockito.given;
 ...  
     private static MockedStatic<Console> console;
 ​
     @BeforeAll
     public static void setUp() {
         console = mockStatic(Console.class);
     }
 ​
     @AfterAll
     public static void tearDown() {
         console.close();
     }
 ...
     @Test
     void 자동차들의_이름을_입력받고_정상적으로_반환한다() {
         // given
         String CORRECT_USER_INPUT = "pobi,woni";
         given(Console.readLine()).willReturn(CORRECT_USER_INPUT);
 ​
         // when
         String actual = View.requestCarNames();
 ​
         // then
         assertThat(actual).isEqualTo(CORRECT_USER_INPUT);
     }

---- int 테스트

1. List의 모든 원소에 대한 확인 하는 테스트

 assertThat(resultList).allMatch((value) -> {
     return value.equals(true);
 });

---- List 테스트

1. List의 모든 원소에 대한 확인 하는 테스트

 assertThat(resultList).allMatch((value) -> {
     return value.equals(true);
 });

2. 두개의 List가 같은지(Equals) 확인하는 테스트

assertThat(result).containsExactly(1, 2, 3, 4, 5, 6);
 List<> list = new List<List.of(value_1, value_2, value_3)>;
 List<> targetList = new List<>;
 ​
 assertThat(list).isEqualTo(targetList);
       
 // containsOnly 방식, 순서X, 오직 이항목만 포함
 assertThat(list).containsOnly( value_1, value_2, value_3 );
 ​
 // containsExactly 방식, 순서O, 오직 이항목만
 assertThat(list).containsExactly( value_1, value_2, value_3 );
 ​
 // allMatch() 방식, 각 항목에 함수 적용
 assertThat(list).allMatch(( car -> Car.getName() == "name" ));
 ​
 // extracting 방식, 특정 원소의 값을 추출하여 리스트로 생성
 assertThat(list).extracting(Car::toString)
                 .containsOnly("value_1", "value_2", "value_3");
 ​
 // filteredOn 방식, 속성의 이름의 조건으로 필터를 한다. || notIn(): 제외하여 필터.
 assertThat(list).filteredOn("name(원하는객체의_속성이름)", notIn("value_3"))
                 .containsOnly(value_1);
 ​
 // filteredOn 방식, 속성의 이름의 조건으로 필터를 한다. || In(): 이것만 포함하여 필터.
 assertThat(list).filteredOn("name", in("value_1"))
                 .containsOnly(value_1);
 ​

3. containAll()의 예상한 요소가 모두 포함되어 있는지 확인

 // containAll() 방식 
 // ResultList가 trueList의 모든 요소를 포함하는지 확인
  assertThat(ResultList).containsAll(trueList);

4. 두개의 List가 다른지(NotEquals) 확인하는 테스트

 assertNotEquals(resultList_1, resultList_2);

---- String 테스트

1. 문자열을 비교하는 테스트

 assertThat( (String)result).contains("1");

2. charAt 메서드로 특정 위치의 문자 찾기

     @Test
     void charAt_메서드로_특정_위치의_문자_찾기() {
         String input = "abc";
         char charAtElement = input.charAt(0);
         assertThat(charAtElement).isEqualTo('a');
     }

3. substring 메서드를 이용한 특정 구간 값을 반환

    @Test
     void substring_메서드로_특정_구간_값을_반환() {
         String input = "(1,2)";
         String result = input.substring(1, 4);
 ​
         assertThat(result).isEqualTo("1,2");
     }

4. 문자열에 대한 검증

 @Test
 void 문자열_검증() {
  String expression = "This is a string";
  assertThat(expression).startsWith("This").endsWith("string").contains("a");
 }

---- Boolean 테스트

1. boolean을 확인하는 테스트

  // true
  assertThat( resultList_1.equals(resultList_2) ).isTrue();
 ​
  assertTrue( (boolean)isTrue );
 ​
  // false
  assertThat( resultList_1.equals(resultList_2) ).isFalse();
 ​

---- Enum 테스트

 @ParameterizedTest
 //          value = 이넘의 이금 정의    names =  이넘의 값을 정의
 @EnumSource(value = TimeUnit.class, names = {"DAYS", "HOURS"})
 void testWithEnumSourceInclude(TimeUnit timeUnit) {
   
    assertTrue(EnumSet.of(TimeUnit.DAYS, TimeUnit.HOURS).contains(timeUnit));
 }

---- contains 포함 관계

1. contains 하나라도 포함이 되어있으면 테스크 통과

하나라도 포함이 되어있으면 테스크 통과

 // **하나라도 포함이 되어있으면 테스크 통과**
 ​
         String[] result = new String[]{1, 2, 3, 4, 5}
 ​
         assertThat(result).contains("1");

2. containsAnyOf 이중 하나가 포함되어있다.

이중 하나가 포함되어있다.

     @Test
     void a_few_simple_assertions() {
         assertThat("The Lord of the Rings")
           
             // 널이 아니고
             .isNotNull()
           
             // The로 시작하고
             .startsWith("The")
           
             // Lord가 포함하고
             .contains("Lord")
             .contains("of")
           
             // containsAnyOf 이중에 하나가 포함되었다. 
             .containsAnyOf("the", "rings")
           
             // 끝이 Rings이다.
             .endsWith("Rings");
     }
     @Test
     void a_few_simple_assertions_2() {
         assertThat("123")
             // 널이 아니고
             .isNotNull()
           
             // The로 시작하고
             .startsWith("1")
           
             // Lord가 포함하고
             .contains("3")
             .contains("2")
           
             // 이중에 하나가 포함되었다.
             .containsAnyOf("1", "aslkdjasd", "아무단어")
           
             // 끝이 Rings이다.
             .endsWith("3");
     }

3. containsExactly. 순서와 정확하게 포함 / 순서 갯수 정확

순서와 정확하게 포함이 되었는가 확인 / 순서 갯수 정확

 @Test
     // 정확하게 포함이 되었는가 확인
 void split_메서드로_주어진_값을_구분_2() {
     String input = "1,,23";
     String[] result = input.split(",");
 ​
     assertThat(result).contains("1");
 ​
     // 정확하게 들어있는가
     assertThat(result).containsExactly("1", "", "3");
 }

4. containsOnly - 순서, 중복을 무시, 원소값과 갯수가 정확히 일치

순서, 중복을 무시하는 대신 원소값과 갯수가 정확히 일치 / 갯수

 //  이름을 가져와서 a 가 포함되어 있는 객체들만 필터링을 하고 그 객체를 검증한다.
 assertThat(list).filteredOn(human -> 
                             human.getName()
                             .contains("a"))
                             .containsOnly(park, jack);

---- filterOn 필터링

1. 필터링하고 객체 검증

 //  이름을 가져와서 a 가 포함되어 있는 객체들만 필터링을 하고 그 객체를 검증한다.
 assertThat(list).filteredOn(human ->  
                             human.getName().contains("a"))
                             // 원소값과 갯수가 정확히 일치
                             .containsOnly(jack, anyName, park);

2. 객체의 멤버변수의 값 검증

 Human park = new Human("Park", 25);
 Human lee = new Human("Lee", 25);
 Human amy = new Human("Amy", 22);
 ​
 List<Human> list = new ArrayList<>();
 ​
 assertThat(list).filteredOn("age", 25)
                 .containsOnly(park, lee);

---- extracting 객체의 프로퍼티 추출하기 후 리스트 (멤버변수)

1. extracting을 이용한 객체 프로퍼트 추출 멤버변수 추출

 List<Human> list = new ArrayList<>();
 Human kim = new Human("Kim", 22);
 Human park = new Human("Park", 25);
 Human lee = new Human("Lee", 25);
 ​
 list.add(kim);
 list.add(park);
 list.add(lee);
 ​
 // contains 포함되어 있는지 확인합니다.
 assertThat(list).extracting("name").contains("Kim", "Park", "Lee");
 assertThat(list).extracting("age").contains(22, 25, 25);
 ​
 // 검증의 타입을 강하게 설정할때 유용하다.
 assertThat(list).extracting("name", String.class).contains("Kim", "Park", "Lee", "Amy", "Jack");
 ​
 // tuple을 이용한 강한 검증방법
 assertThat(list).extracting("name", "age")
      .contains(
         tuple("Kim", 22),
         tuple("Park", 25),
         tuple("Lee", 25)
             );
 package study;
 ​
 import static org.assertj.core.api.Assertions.*;
 import static org.junit.jupiter.api.Assertions.*;
 ​
 import org.assertj.core.api.StringAssert;
 import org.junit.jupiter.api.DisplayName;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.function.Executable;
 import org.junit.jupiter.params.ParameterizedTest;
 import org.junit.jupiter.params.provider.*;
 ​
 import java.io.*;
 import java.util.*;
 import java.util.concurrent.TimeUnit;
 ​
 // https://assertj.github.io/doc/#overview-what-is-assertj
 public class StringTest {
 ​
     @Test
     void split_메서드로_주어진_값을_구분() {
         String input = "1,2";
         String[] result = input.split(",");
 ​
         assertThat(result).contains("2", "1");
 ​
         // 특정 결과가 주어진 순서대로 "1"과 "2"를 포함하는지 확인
         assertThat(result).containsExactly("1", "2");
     }
 ​
     // TODO: 10/31/23 활용해보기.
     @Test
     void split_메서드_사용시_구분자가_포함되지_않은_경우_값을_그대로_반환() {
         String input = "1";
         String[] result = input.split(",");
 ​
         assertThat(result).contains("1");
     }
 ​
     @Test
     void substring_메서드로_특정_구간_값을_반환() {
         String input = "(1,2)";
         String result = input.substring(1, 4);
 ​
         assertThat(result).isEqualTo("1,2");
     }
 ​
     @Test
     void charAt_메서드로_특정_위치의_문자_찾기() {
         String input = "abc";
         char charAtElement = input.charAt(0);
         assertThat(charAtElement).isEqualTo('a');
     }
 ​
     @Test
     void charAt_메서드_사용시_문자열의_길이보다_큰_숫자_위치의_문자를_찾을_때_예외_발생() {
         String input = "abc";
 ​
         assertThatThrownBy(() -> input.charAt(5))
                 .isInstanceOf(StringIndexOutOfBoundsException.class)
                 .hasMessageContaining("String index out of range: 5");
     }
 ​
     @DisplayName("CsvSource을 이용한 테스트 활용")
     @ParameterizedTest
     @CsvSource({"abc,1,true", "def,2,false"})
     public void CsvSource_활용하여_여러개_테스트_확인하기(String str, int num, boolean isTrue) throws Exception {
         assertThat(str).contains(str);
         assertThat(num).isEqualTo(num);
         assertThat(isTrue).isEqualTo(isTrue);
     }
 ​
     @ParameterizedTest(name = "매개변수") // 무조건 name으로
     @ValueSource(strings = {"abc", "def", "ghi"})
     @DisplayName("ValueSource을 이용한 테스트 활용_1")
     void ValueSource_을_활용하여_여러개_테스트_확인하기_문자(String str) {
         assertThat(str).contains(str);
     }
 ​
     @ParameterizedTest(name = "매개변수") // 무조건 name으로
     @ValueSource(ints = {1, 2, 3})
     @DisplayName("ValueSource을 이용한 테스트 활용_2")
     void ValueSource_을_활용하여_여러개_테스트_확인하기_숫자(int num) {
         assertThat(num).isEqualTo(num);
     }
 ​
     @ParameterizedTest(name = "매개변수") // 무조건 name으로
     @ValueSource(booleans = {true, false})
     @DisplayName("ValueSource을 이용한 테스트 활용_3")
     void ValueSource_을_활용하여_여러개_테스트_확인하기_불(boolean isTrue) {
         assertThat(isTrue).isEqualTo(isTrue);
     }
 ​
     @ParameterizedTest
     @NullSource
     @EmptySource
     @DisplayName("NullSource, EmptySource의 에너테이션을 이용한 NULL, EMPTY 테스트")
     void NULL_EMPTY_을_활용한_테스트(String text) {
         assertTrue(text == null || text.trim().isEmpty());
     }
 ​
     @ParameterizedTest
     @NullAndEmptySource
     @DisplayName("NullAndEmptySource의 에너테이션을 이용한 NULL, EMPTY 테스트")
     @ValueSource(strings = {" ", "   ", "\t", "\n"})
     void NullAndEmptySource_을_활용한_테스트(String text) {
         assertTrue(text == null || text.trim().isEmpty());
     }
 ​
 ​
     @DisplayName("assertThrows_을_이용한_예외를_테스트")
     @Test
     void assertThrows_을_이용한_예외를_테스트() {
         String input = "abc";
 ​
         // when then
         assertThrows(StringIndexOutOfBoundsException.class, () -> input.charAt(5));
     }
 ​
     @DisplayName("assertThatThrownBy_을_이용한_예외를_테스트")
 ​
     @Test
     public void assertThatThrownBy_을_이용한_예외를_테스트() {
         assertThatThrownBy(() -> {
             throw new Exception("boom!");
         }).isInstanceOf(Exception.class)
                 .hasMessageContaining("boom");
     }
 ​
     @DisplayName("assertThatExceptionOfType_을_이용한_예외를_테스트")
     @Test
     public void assertThatExceptionOfType_을_이용한_예외를_테스트() {
         assertThatExceptionOfType(IOException.class)
                 .isThrownBy(() -> {
                     throw new IOException("boom!");
                 })
                 .withMessage("%s!", "boom") // 예외 메시지가 "boom!"과 일치하는지 확인하고
                 .withMessageContaining("boom") // 메시지에 "boom"이 포함되어 있는지 확인하고
                 .withNoCause(); // 예외가 특정 원인을 갖지 않는지 확인한다.
 ​
 //        코드에서 withMessage("%s!", "boom")는
 //        문자열 "boom"을 %s 자리 표시자에 대체하고,
 //        그 뒤에 느낌표를 추가하여 "boom!"이라는 문자열을 생성합니다.
     }
 ​
     @DisplayName("out_출력_확인_테스트")
     @Test
     void out_출력_확인_테스트() {
         // given
         // 출력을 위한 테스트
         ByteArrayOutputStream outContent = new ByteArrayOutputStream();
         System.setOut(new PrintStream(outContent));
 ​
         // when
         System.out.println();
 ​
         // than
         assertThat(outContent.toString()).isEqualTo("\n");
     }
 ​
     @DisplayName("List의_모든_원소에_대한_확인_하는_테스트")
     @Test
     public void List의_모든_원소에_대한_확인_하는_테스트() throws Exception {
         // given
         List<Integer> resultList = List.of(1, 1, 1, 1, 1);
 ​
         // then
         assertThat(resultList).allMatch((value) -> {
             return value.equals(1);
         });
     }
 ​
     @DisplayName("두개의_List가_같은지_확인하는_테스트")
     @Test
     void 두개의_List가_다른지_확인하는_테스트() throws Exception {
         //given
         List<Integer> resultList_1 = List.of(1, 2, 3);
         List<Integer> resultList_2 = List.of(3, 4, 5);
 ​
         // then
         // 방법 1 assertNotEquals
         assertNotEquals(resultList_1, resultList_2);
         // 방법 2 assertThat().isFalse
         assertThat(resultList_1.equals(resultList_2)).isFalse();
     }
 ​
     @DisplayName("두개의_List가_같은지_확인하는_테스트")
     @Test
     void 두개의_List가_같은지_확인하는_테스트() throws Exception {
         //given
         List<Integer> resultList_1 = List.of(1, 2, 3);
         List<Integer> resultList_2 = List.of(1, 2, 3);
 ​
         // then
         // 방법 1 assertEquals
         assertEquals(resultList_1, resultList_2);
         // 방법 2 assertThat().isTrue
         assertThat(resultList_1.equals(resultList_2)).isTrue();
     }
 ​
     @DisplayName("문자열_검증")
     @Test
     void 문자열_검증() {
         String expression = "This is a string";
         assertThat(expression).startsWith("This").endsWith("string").contains("a");
     }
 ​
     @DisplayName("EnumSource_을_이용한_ENUM_포함에_관한_테스트")
     @ParameterizedTest
 //          value = Enum의 클래스 정의  names =  Enum의 값을 정의
     @EnumSource(value = TimeUnit.class, names = {"DAYS", "HOURS"})
     void EnumSource_을_이용한_ENUM_포함에_관한_테스트(TimeUnit inputTimeUnit) {
         assertTrue(
                 EnumSet.of(TimeUnit.DAYS, TimeUnit.HOURS).contains(inputTimeUnit)
         );
     }
 ​
     @DisplayName("contains_을_활용한_하나라도_포함이_되어있으면_테스트_통과")
     @Test
     public void contains_을_활용한_하나라도_포함이_되어있으면_테스트_통과() throws Exception {
         String input = "1,,23";
         String[] result = input.split(",");
 ​
         assertThat(result).contains("1");
         assertThat(result).contains("1", "23");
     }
 ​
     @DisplayName("containsAnyOf_을_활용한_이중에_하나가_포함되어있으면_테스트_통과_1")
     @Test
     void containsAnyOf_을_활용한_이중에_하나가_포함되어있으면_테스트_통과_1() {
         assertThat("The Lord of the Rings")
                 // 널이 아니고
                 .isNotNull()
                 // The로 시작하고
                 .startsWith("The")
                 // Lord가 포함하고
                 .contains("Lord")
                 .contains("of")
                 // 이중에 하나가 포함되었다.
                 .containsAnyOf("the", "rings")
                 // 끝이 Rings이다.
                 .endsWith("Rings");
     }
 ​
     @DisplayName("containsAnyOf_을_활용한_이중에_하나가_포함되어있으면_테스트_통과_1")
     @Test
     void containsAnyOf_을_활용한_이중에_하나가_포함되어있으면_테스트_통과_2() {
         assertThat("123")
                 // 널이 아니고
                 .isNotNull()
                 // The로 시작하고
                 .startsWith("1")
                 // Lord가 포함하고
                 .contains("3")
                 .contains("2")
                 // 이중에 하나가 포함되었다.
                 .containsAnyOf("1", "aslkdjasd", "아무단어")
                 // 끝이 Rings이다.
                 .endsWith("3");
     }
 ​
     @DisplayName("containsExactly_을_활용한_정확한_순서로_리스트에_포함되어_있는가_테스트")
     @Test
     void containsExactly_을_활용한_정확한_순서로_리스트에_포함되어_있는가_테스트() {
         String input = "1,,23";
         String[] result = input.split(",");
 ​
         // 정확하게 들어있는가
         assertThat(result).containsExactly("1", "", "23");
     }
 ​
     @DisplayName("containsOnly_을_활용하여_원소값과_갯수가_정확히_일치_테스트")
     @Test
     public void containsOnly_을_활용하여_원소값과_갯수가_정확히_일치_테스트() throws Exception {
         List<String> list = List.of("apple", "banana", "orange", "americano");
 ​
         //  이름을 가져와서 a 가 포함되어 있는 객체들만 필터링을 하고 그 객체를 검증한다.
         assertThat(list)
                 .filteredOn(value -> value.contains("apple")) // 'apple'를 포함하는 객체들을 필터링하고
                 .containsOnly("apple"); // 'banana'과 'orange'만을 포함하는지 검증한다.
     }
 ​
     @DisplayName("filterOn_을_활용하여_원소값과_갯수가_정확히_일치_테스트")
     @Test
     public void filterOn_필터링하고_객체_검증_일치_테스트() throws Exception {
         List<String> list = List.of("apple", "banana", "orange", "americano");
 ​
         //  이름을 가져와서 a 가 포함되어 있는 객체들만 필터링을 하고 그 객체를 검증한다.
         assertThat(list)
                 .filteredOn(value -> value.contains("apple")) // 'apple'를 포함하는 객체들을 필터링하고
                 .containsOnly("apple"); // 'banana'과 'orange'만을 포함하는지 검증한다.
     }
 ​
     @DisplayName("기본적인_assertions_사용법")
     @Test
     public void 기본적인_assertions_사용법() throws Exception {
         Human frodo = new Human("Frodo");
         Human sauron = new Human("Sauron");
 ​
         // basic assertions
         assertThat(frodo.getName()).isEqualTo("Frodo");
         assertThat(frodo).isNotEqualTo(sauron);
 ​
         // chaining string specific assertions
         assertThat(frodo.getName()).startsWith("Fro")
                 .endsWith("do")
                 .isEqualToIgnoringCase("frodo");
     }
 ​
     @DisplayName("collection_에_Assertions_적용하기")
     @Test
     public void collection_에_Assertions_적용하기() throws Exception {
         // given
         Human frodo = new Human("Frodo");
         Human sam = new Human("Sam");
         Human seung = new Human("Seung");
         Human chan = new Human("Chan");
         Human sauron = new Human("Sauron");
 ​
         List<Human> fellowshipOfTheRing = List.of(frodo, sam, seung, chan);
 ​
         // than
         assertThat(fellowshipOfTheRing).hasSize(4)
                 .contains(frodo, sam)
                 .doesNotContain(sauron);
     }
 ​
     @DisplayName("as_을_활용한_메세지보내기")
     @Test
     public void as_을_활용한_메세지보내기() throws Exception {
         // given
         Human frodo = new Human("Frodo");
 ​
         // then
         // as()는 테스트를 설명하는 데 사용되며 오류 메시지 앞에 표시됩니다.
         // 오류일때 as() 발동
         // ex) org.opentest4j.AssertionFailedError: [check Frodo's age]
         assertThat(frodo.getAge()).as("check %s's age", frodo.getName()).isEqualTo(33);
         assertThat(frodo.getAge()).as("check %s's age", frodo.getAge()).isEqualTo(33);
         assertThat(frodo.getAge()).as("check %s's age", frodo.isTrue()).isEqualTo(33);
     }
 ​
     @DisplayName("Satisfy_을_활용한_assertions_List_확인하기")
     @Test
     public void Satisfy_을_활용한_assertions_List_확인하기() throws Exception {
         // given
         Human frodo = new Human("Frodo");
         Human sam = new Human("Sam");
         Human pippin = new Human("Pippin");
 ​
         List<Human> humansList = List.of(frodo, sam, pippin);
 ​
         // than
         // 모든 요소는 주어진 주장을 만족해야 합니다.
         assertThat(humansList).allSatisfy(human -> {
             assertThat(human.getName()).isNotEqualTo("Sauron");
         });
 ​
         // 최소한 하나의 요소가 주어진 주장을 만족해야 합니다.
         assertThat(humansList).anySatisfy(human -> {
             assertThat(human.getName()).isEqualTo("Sam");
         });
 ​
         // 어떤 요소도 주어진 주장을 만족해서는 안 됩니다.
         assertThat(humansList).noneSatisfy(human ->
                 assertThat(human.getName()).isEqualTo("NOTTING")
         );
     }
 ​
     @DisplayName("allMatch_anyMatch_noneMatch_을_활용한_테스트")
     @Test
     public void allMatch_anyMatch_noneMatch_을_활용한_테스트() throws Exception {
         // given
         Human frodo = new Human("Frodo");
         Human sam = new Human("Sam");
         Human pippin = new Human("Pippin");
 ​
         List<Human> humansList = List.of(frodo, sam, pippin);
 ​
         // than
         assertThat(humansList).
                 // 모든 요소는 주어진 주장을 만족해야 합니다.
                         allMatch(human -> human.getAge() == 33, "age")
 ​
                 // 모든 요소는 주어진 주장을 만족해야 합니다.
                 .allMatch(human -> human.getAge() == 33)
 ​
                 // 최소한 하나의 요소가 주어진 주장을 만족해야 합니다.
                 .anyMatch(character -> character.getName().contains("pp"))
 ​
                 // 어떤 요소도 주어진 주장을 만족해서는 안 됩니다.
                 .noneMatch(character -> character.getName() == "NOTTING");
     }
 ​
     @DisplayName("element_index_을_활용한_테스트")
     @Test
     public void element_index_을_활용한_테스트() throws Exception {
         // given
         Human frodo = new Human("Frodo");
         Human sam = new Human("Sam");
         Human pippin = new Human("Pippin");
 ​
         List<Human> humansList = List.of(frodo, sam, pippin);
 ​
         // then
         // index을 활용한 List 탐색
         assertThat(humansList).first().isEqualTo(frodo);
         assertThat(humansList).element(1).isEqualTo(sam);
         assertThat(humansList).last().isEqualTo(pippin);
 ​
         // 강력한 문자열 TYPE Assertions 테스트
         Iterable<String> humanName = List.of("frodo", "sam", "pippin");
 ​
         // STRING is 문자표식
         // as()은 가독성을 위한 표식
         assertThat(humanName).first(as(STRING))
                 .startsWith("fro")
                 .endsWith("do");
 ​
         assertThat(humanName).element(1, as(STRING))
                 .startsWith("sa")
                 .endsWith("am");
 ​
         assertThat(humanName).last(as(STRING))
                 .startsWith("pip")
                 .endsWith("pin");
 ​
         // 강력한 TYPE Assertions 테스트
         assertThat(humanName, StringAssert.class).first()
                 .startsWith("fro")
                 .endsWith("do");
     }
 ​
     @DisplayName("단일_요소_list_의_테스트_방법")
     @Test
     public void 단일_요소_list_의_테스트_방법() throws Exception {
         // given
         List<String> oneList = List.of("Maggie");
 ​
         // then
         // 단일 요소의 리스트인지 확인한다.
         assertThat(oneList).singleElement()
                 .isEqualTo("Maggie");
 ​
         // 단일 요소의 리스트 확인 => 문자열 테스트.
         assertThat(oneList).singleElement(as(STRING))
                 .endsWith("gie");
 ​
         // 단일 요소확인 => 문자열 TYPE 확인
         assertThat(oneList, StringAssert.class).singleElement()
                 .startsWith("Mag");
     }
 ​
     @DisplayName("속성_property_필드_field_in_notIn_not_값을_확인하는_메서드_테스트")
     @Test
     public void 속성_property_필드_field_in_notIn_not_값을_확인하는_메서드_테스트() throws Exception {
         // given
         Human frodo = new Human("Frodo");
         Human frodo2 = new Human("Frodo2");
         Human sam = new Human("Sam");
         Human sam2 = new Human("Sam2");
         Human pippin = new Human("Pippin");
         Human pippin2 = new Human("Pippin2", 12);
 ​
         List<Human> humansList = List.of(frodo, sam, pippin, frodo2, sam2, pippin2);
 ​
         // then
         // filter을 사용을 한다. 속성의 이름(property), 멤버 변수(field)의 이름정의
         assertThat(humansList).filteredOn("age", 33)
                 .filteredOn("name", "Frodo")
                 .containsOnly(frodo);
 ​
 //        // filter에서 여러 속성 확인하는 방법 notIn()
         assertThat(humansList)
                 .filteredOn("name", notIn("Sam", "Sam2", "Pippin", "Pippin2"))
                 .containsOnly(frodo, frodo2);
 ​
         // filter에서 여러 속성 확인하는 방법 in()
         assertThat(humansList).filteredOn("name", in("Frodo", "Frodo2"))
                 .containsOnly(frodo, frodo2);
 ​
         // filter에서 하나만 아니다.
         assertThat(humansList).filteredOn("name", not("Sam"))
                 .containsOnly(frodo, pippin, frodo2, sam2, pippin2);
 ​
 //        // 여러 필드 사용하기.
         assertThat(humansList).filteredOn("name", "Pippin")
                 .filteredOn("age", not(12))
                 .containsOnly(pippin);
     }
 ​
     @DisplayName("filter_을_활용한_미만_값을_필터하는_방법")
     @Test
     public void filter_을_활용한_미만_값을_필터하는_방법() throws Exception {
         // given
         Human frodo = new Human("Frodo", 12);
         Human sam = new Human("Sam", 20);
         Human pippin = new Human("Pippin", 30);
 ​
         List<Human> humansList = List.of(frodo, sam, pippin);
 ​
         // then
         // 25 미만 isLessThan
         assertThat(humansList).filteredOnAssertions(human ->
                         assertThat(human.getAge()).isLessThan(25))
                 .containsOnly(frodo, sam);
 ​
         // 20 이하 isLessThanOrEqualTo
         assertThat(humansList).filteredOnAssertions(human ->
                         assertThat(human.getAge()).isLessThanOrEqualTo(20))
                 .containsOnly(frodo, sam);
 ​
         // 25 초과 isGreaterThan
         assertThat(humansList).filteredOnAssertions(human ->
                         assertThat(human.getAge()).isGreaterThan(25))
                 .containsOnly(pippin);
 ​
     }
 ​
     @DisplayName("extracting_을_활용한_속성_추출_방법")
     @Test
     public void extracting_을_활용한_속성_추출_방법() throws Exception {
         // given
         Human kawhi = new Human("kawhi", 25);
         Human seung = new Human("seung", 25);
         Human chan = new Human("chan", 25);
         List<Human> humanList = new ArrayList<>();
         humanList.add(kawhi);
         humanList.add(seung);
         humanList.add(chan);
 ​
         // than
         // 추출한 것의 따로 리스트를 만든다.
         assertThat(humanList).extracting("name")
                 .containsOnly(kawhi.getName(), seung.getName(), chan.getName()); // DOES NOT COMPILE
 ​
         // Use Object assertions like isEqualTo
         assertThat(humanList).extracting(Human::getName)
                 .containsOnly(kawhi.getName(), seung.getName(), chan.getName()); // DOES NOT COMPILE
 ​
     }
 ​
 }
 ​
 class Human {
     private String name;
     private int age;
     private boolean isTrue;
 ​
     public Human(String name) {
         this.name = name;
         age = 33;
         isTrue = true;
     }
 ​
     public Human(String name, int aage) {
         this.name = name;
         age = aage;
         isTrue = true;
     }
 ​
     public String getName() {
         return name;
     }
 ​
     public int getAge() {
         return age;
     }
 ​
     public boolean isTrue() {
         return isTrue;
     }
 ​
     @Override
     public String toString() {
         return name + " " + age;
     }
 }

1. CreateRandomNumTest

  • 0~9까지 랜덤 숫자을 반환하는 클래스

✅ 요구 사항 및 기능 목록표

  • 랜덤 정수 중 한개의 정수를 생성해주는 클래스.
    • 0에서 9까지의 정수 중 한 개의 정수 반환.

🤔 생각

  • int로 반환하는 경우 1000번의 실행중 랜덤 함수의 반환값의 범위가 0부터 9까지 맞는지 검증한다.
  • assertTrue()을 이용하여 범위의 값이 맞는지 주장한다.

🚀 테스트 구현

     void createRanOneNumTest() {
         // given
         int testCount = 1000;
         int result = 0;
 ​
         // when
         for (int i = 0; i < testCount; i++) {
             result = createRandomNum.createRanOneNum();
 ​
             // then
             assertTrue(result >= 0 && result <= 9);
         }
     }

2. RaceCarTest

  • 레이스 게임에 참여하는 자동차

✅ 요구 사항 및 기능 목록표

  • 자동차의 이동을 제어해준다.
    • 전진하는 조건은 0에서 9 사이에서 무작위 값을 구한 후 무작위 값이 4 이상일 경우이다.

🤔 생각

  • 자동자을 움직인후
  • 움직인 횟수와 움직인 후 반환하는 '-'의 문자열을 확인하여 검증을 한다.
  • isEqualTo, extracting().containsExactily을 이용하여 검증
  • 자동차를 한번 두번 세번 각각의 테스트를 한다.

🚀 테스트 구현

 @Test
     void movementControlCar() {
         // when
         raceCar1.movementControl(MOVE);
         raceCar1.movementControl(MOVE);
         raceCar1.movementControl(STOP);
 ​
         int resultNum = raceCar1.getCntMovement();
         String rasultStr = raceCar1.toStringMoveState();
 ​
         // then
         assertThat(resultNum).isEqualTo(2);
         assertThat(rasultStr).isEqualTo("--");
     }
 ​
  void moveForward() {
         // given
         List<RaceCar> list = List.of(raceCar1, raceCar2, raceCar3);
 ​
         // when
         for (int i = 0; i < 3; i++) {
             list.forEach(raceCar -> raceCar.moveForward());
         }
 ​
         // then
         // car의 toString을 추출하여 따로 리스트를 만든다.
         assertThat(list).extracting(RaceCar::toString).containsExactly("Test", "seung", "chan");
     }

✅ 요구 사항 및 기능 목록표

  • 자동차의 움직여도 되는 조건 이 맞는가. (4이상, 9이하)

🤔 생각

  • 0이면 이동이 없고 4이면 이동을 한다.
  • 이동을 하면 isTrue(), 이동을 안하면 isFale() 메서드를 사용해 검증을 한다.

🚀 테스트 구현

    @DisplayName("자동차의 움직여도 되는 조건 이 맞는가. (4이상, 9이하)")
     @Test
     void isMovementCondition() {
         // when
         boolean result = raceCar1.isMovement(MOVE);
 ​
         // then
         assertThat(result).isTrue();
     }

2. GameHostTest

  • 게임의 진행 상황을 알려주는 클래스 - 게임의 진행자이다.

✅ 요구 사항 및 기능 목록표

  • 자동차 경주 게임을 완료한 후 누가 우승했는지를 알려준다.
    • 우승자는 한 명 이상일 수 있다.
    • "-" 가 시도할 횟수만큼 있으면 우승

🤔 생각

  • 각각의 자동차들을 이동을 한후
  • 승리를 한 자동차들의 리스트를 얻는다.
  • containsOnly, containsExactly, allMatch, isEqualTo, extracting(), filteredOn을 이용하여 검증

🚀 테스트 구현

   @DisplayName("자동차 경주 게임을 완료한 후 누가 우승했는지를 알려준다. ")
     @Test
     void knowWinRaceCarsTest() {
         // given
         moveRaceCar(raceCar_1);
 ​
         moveRaceCar(raceCar_2);
 ​
         moveRaceCar(raceCar_3);
 ​
         // when
         List<RaceCar> winners = gameHost.giveWinnerList(progressRaceCarList);
 ​
         // than
         // isEqualTO 방식
         assertThat(winners).isEqualTo(List.of(raceCar_1, raceCar_2, raceCar_3));
       
         // containsOnly 방식, 순서X, 오직 이항목만 포함
         assertThat(winners).containsOnly(raceCar_1, raceCar_2, raceCar_3);
         
         // containsExactly 방식, 순서O, 오직 이항목만
         assertThat(winners).containsExactly(raceCar_1, raceCar_2, raceCar_3);
         
         // allMatch() 방식, 각 항목에 함수 적용
         assertThat(winners).allMatch((raceCar -> raceCar.getCntMovement() == 1));
         
         // extracting 방식, 특정 원소의 값을 추출하여 리스트로 생성
         assertThat(winners).extracting(RaceCar::toString)
                 .containsOnly(raceCar_1.toString(), raceCar_2.toString(), raceCar_3.toString());
         
         // filteredOn 방식, 속성의 이름의 조건으로 필터를 한다. || notIn(): 제외하여 필터.
         assertThat(winners).filteredOn("name", notIn("third"))
                 .containsOnly(raceCar_1);
 ​
         // filteredOn 방식, 속성의 이름의 조건으로 필터를 한다. || In(): 이것만 포함하여 필터.
         assertThat(winners).filteredOn("name", in("fir"))
                 .containsOnly(raceCar_1);
     }

✅ 요구 사항 및 기능 목록표

  • 현제 레이싱을 하는 자동차의 움직인 횟수중에 최고로 많은 횟수를 알려준다.

🤔 생각

  • 3개의 자동차의 이동을 다양하게 실행을 한다.
  • 자동차의 리스트중에 최고로 많은 회수를 예상하는 값과 isEqualTo()메서드로 검증한다.

🚀 테스트 구현

    @DisplayName("현제 레이싱을 하는 자동차의 움직인 횟수중에 최고로 많은 횟수를 알려준다.")
     @Test
     void getCntMaxMoveTest() {
         // given
         moveRaceCar(raceCar_1);
         moveRaceCar(raceCar_1);
         moveRaceCar(raceCar_1);
 ​
         moveRaceCar(raceCar_2);
         moveRaceCar(raceCar_2);
 ​
         moveRaceCar(raceCar_3);
 ​
         // when
         Integer result = gameHost.getMaxCntMoveOfRaceCar(progressRaceCarList);
 ​
         // than
         assertThat(result).isEqualTo(3);
     }

3. JudgeStandard

  • 게임 진행자(Host)가 게임을 판단하는 기준

✅ 요구 사항 및 기능 목록표

  • 자동차중에 전진을 한 숫자
  • 전체의 차중에 이동이 제일 많으면 승리를 한것이다.

🤔 생각

  • 자동차의 이동을 하여
  • 이동한 수와 주어진 숫자가 많다면
  • isTrue()을 이용하여 검증을 하자.

🚀 테스트 구현

     @DisplayName("레이스 게임의 승리 여부 확인._3")
     @Test
     void isVictoryCondition_3() {
         // given
         moveRaceCar(raceCar_1);
         moveRaceCar(raceCar_1);
         moveRaceCar(raceCar_1);
 ​
         // when
         boolean result = judgeStandard.isVictory(raceCar_1, 3);
 ​
         // than
         assertThat(result).isTrue();
     }

4. Utill

  • 모든 클래스에서 사용할 수 있는 메서드의 모음인 Utill 클래스 작성

✅ 요구 사항 및 기능 목록표

  • 값이 1이상인지 확인한다.

🤔 생각

  • 1의 값을 준비를 하여
  • 1이상이면 isTrue(), 미만이면 isFalse()으로 검증한다.

🚀 테스트 구현

     @DisplayName("값이 1이상인지 확인한다. 테스트")
     @Test
     void valueGreaterThanEqualOne() {
         // given
         Integer input = 1;
 ​
         // when
         boolean result = Utill.isGreaterThanAndEqualOne(input);
 ​
         // than
         assertThat(result).isTrue();
     }

✅ 요구 사항 및 기능 목록표

  • num_1 과 num_2의 숫자가 같으면 true 테스트

🤔 생각

  • 2개의 숫자를 제시한다.
  • isTrue()을 이용해 값을 검증한다.

🚀 테스트 구현

     @DisplayName("num_1 과 num_2의 숫자가 같으면 true 테스트")
     @Test
     void isSameNum() {
         // given
         int num_1 = 1;
         int num_2 = 1;
 ​
         // when
         boolean result = Utill.isSameNum(num_1, num_2);
 ​
         // than
         assertThat(result).isTrue();
     }

✅ 요구 사항 및 기능 목록표

  • 배열을 리스트로 만들어준다. 테스트

🤔 생각

  • 검증할 수 있는 Integer 타입의 배열을 생성을 하여
  • 배열을 리스트로 생성을 하는 메서드를 호출하여 배열을 원하는 리스트로 만든다.
  • 목적의 리스트와 생성한 리스트을 비교
  • isEqualTo, containsOnly, containsExactly을 이용해 검증한다.

🚀 테스트 구현

     @DisplayName("배열을 리스트로 만들어준다. 테스트")
     @Test
     void makeListFromArr() {
         // given
         Integer[] inputArr = new Integer[]{1, 2, 3};
         List<Integer> targetList = new ArrayList<>();
         targetList.add(1);
         targetList.add(2);
         targetList.add(3);
 ​
         // when
         List<Integer> resultList = Utill.makeListFromArr(inputArr);
 ​
         // than
         // isEqualTo의 방법
         assertThat(resultList).isEqualTo(targetList);
       
         // containsOnly의 방법
         assertThat(resultList).containsOnly(targetList);
       
         // containsExactly의 방법
         assertThat(resultList).containsExactly(targetList);
     }

✅ 요구 사항 및 기능 목록표

  • print 메서드 테스트

🤔 생각

  • 출력을 테스트를 하는 방법을 생각하자.
  •  ByteArrayOutputStream outContent = new ByteArrayOutputStream();
     System.setOut(new PrintStream(outContent));
    을 이용하여 문자열 검증하는 방법을 공부해보자.

🚀 테스트 구현

 ...
     private ByteArrayOutputStream outContent;
 ​
     @BeforeEach
     void setUp() {
         outContent = new ByteArrayOutputStream();
         System.setOut(new PrintStream(outContent));
     }
 ...
     @DisplayName("print 메서드 테스트")
     @Test
     void print() {
         // given
         String input = "input";
 ​
         // when
         Utill.print(input);
 ​
         // than
         assertThat(outContent.toString()).isEqualTo("input\n");
     }