테스트 코드를 사용을 하는 이유는 저의 생각에는....
코드의 메서드를 작성하면서 클래스 간의 종속성을 고려하는 시간은 매우 중요합니다. 테스트 코드를 작성하면서 각 클래스의 종속성과 독립성을 고려하는 것은 큰 의미가 있다고 생각합니다.
또한, 메서드에 두 가지 이상의 책임을 할당할 때는 테스트 코드 작성이 어려워 메서드를 분리하는 방향을 잡기가 편했습니다.
테스트 코드를 작성한 메서드와 그렇지 않은 메서드를 비교했을 때 차이를 느끼는 일이 자주 있습니다.
테스트 코드를 작성하지 않은 코드는 불확실성이 생겨 다음 작업을 진행하기가 어려웠습니다. 그러나 테스트 코드를 작성한 메서드는 안심하고 다음 작업을 진행할 수 있게 해줍니다.
테스트 코드를 믿음만으로만 신뢰하는 것보다는 예외 상황에 대비하여 안전장치를 항상 고려하는 것이 중요합니다.
학습 테스트 진행 방식
- 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");
}