Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#22_4 [ParameterizedTest 진행하기] #24

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions TestCode/src/main/java/com/example/testcode/parameterized/Day.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.example.testcode.parameterized;

public enum Day {

Monday("2024-09-30"),
Tuesday("2024-10-01"),
Wednesday("2024-10-02"),
Thursday("2024-10-03"),
Friday("2024-10-04"),
Saturday("2024-10-05"),
Sunday("2024-10-06");


private String date;

Day(String date) {
this.date = date;
}

public String getDate() {
return date;
}
}


Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.example.testcode.parameterized;

import java.time.LocalDate;
import java.util.stream.Stream;

public class FindSunday {


public static LocalDate findFirstSunday(LocalDate date) {
if (date.getDayOfWeek().getValue() == 7) { // 날짜가 일요일인지 확인
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

주석으로 날짜가 일요일인지 확인 을 작성하는 것 보다 코드를 보았을 때 직관적으로 볼 수 있다면 읽기 쉬운 코드를 작성하게 만들어주는게 좋습니다.
대표적으로 모든 "매직넘버"를 상수로 만들거나, 변수로서 이름을 지어주는 것입니다.

이런식으로 작성하면 어떨까요?

Suggested change
if (date.getDayOfWeek().getValue() == 7) { // 날짜가 일요일인지 확인
if (date.getDayOfWeek() == SUNDAY) { // 날짜가 일요일인지 확인

return date; // 만약 해당 날짜가 일요일이면 그 날짜를 반환
}
return findFirstSunday(date.plusDays(1)); // 일요일이 아니면 하루를 더하고 다시 함수 호출
}
Comment on lines +16 to +21
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

재귀함수 사용을 처음 사용하셨네요~ 직접 작성하신건가요?
재귀함수는 잘 사용하면 굉장히 간결한 코드를 만들 수 있어서 이런 시도는 꽤 좋은 방식입니다.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

사실 재귀함수를 사용할수있으면 계속사용해보곳싶었는데
재귀함수가 참 잘 사용하기 어려운 것 같아서 지금까지 사용할 상황을 잘 캐치하지못했었습니다
요번에 반복적인 행위테스트를 진행하다 보니 재귀를 사용할수있을거란 생각을 하고 시도를 하다가 실패하여서..
슬프게도..코드자체는 GPT도움이 있었습니다..
GPT의존도는 많이 줄이긴했는데..완전히 떼지는 못했네요..

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

가능하면 직접 구현해보려고 노력해보세요~ 로직을 짜고 직접 구현하는 과정을 통해서 시간이 걸릴 수 있겠지만, 구현능력을 키우려면 직접 짜보셔야 하기 때문에 연습이 필요합니다 :)


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.example.testcode.parameterized;

import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.ArgumentsProvider;

import java.time.LocalDate;
import java.util.stream.Stream;

public class CustomArgumentsProvider implements ArgumentsProvider {
@Override
public Stream<? extends Arguments> provideArguments(ExtensionContext context) {
return Stream.of(
Arguments.of("2024-09-30"),
Arguments.of("20241001"),
Arguments.of("2024/10/02"),
Arguments.of("2024-10-03")
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
package com.example.testcode.parameterized;

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

import java.time.LocalDate;
import java.util.stream.Stream;

import static org.junit.jupiter.api.Assertions.*;

class FindSundayTest {


@Order(1)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Order 어노테이션은 테스트에 순서를 주는 역할을 합니다.
단위테스트의 경우 순서와 관계없이 작동할 수 있어야 하기 때문에 해당 어노테이션은 지양해서 사용해주세요. 테스트에 순서가 필수가 되면 각 테스트끼리의 의존성이 생기며, 비즈니스로직 하나가 깨지면 연관된 모든 테스트가 깨질 수 있습니다.

따라서 가능한 단위테스트는 독립적으로 실행되어야하며 실행순서에 관련 없이 성공/실패되어야 합니다.

이번 PR은 학습용이기 때문에 우선 이것저것 사용해보시고, 실제 테스트코드 작성시에는 이 어노테이션은 지양해야 한다고 생각해주시면 되겠습니다.

@Test
@DisplayName("정상적인 LocalDate를 입력했을때 일요일을 찾는다. - success")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

테스트명을 더 자세하게 서술하고 있는 부분이 좋습니다 👍
다만, 헷갈릴 여지를 없애는게 좋습니다. 예를 들면 "정상적인 LocalDate"의 기준이 모호할 수 있겠네요. 테스트명은 조건, 행위, 결과가 포함되게 서술하면서 제목만 보고 내용을 유추할 수 있어야 합니다.

"일요일인 날짜를 입력하면 같은 날짜가 리턴된다" 와 같이 비즈니스로직을 표현할 수도 있겠네요.

public void givenLocalDate_whenFindFirstSunday_thenFirstSunday() {
//given
LocalDate date = LocalDate.of(2024, 9, 29);
//when
LocalDate result = FindSunday.findFirstSunday(date);
//then
assertEquals(LocalDate.of(2024, 9, 29), result);

}

@Order(2)
@Test
@DisplayName("날짜 포맷이 맞지않게 12345 입력했을때 실패한다.")
public void givenWrongLocalDateForMat_whenFindFirstSunday_thenFirstSunday() {
//given
LocalDate date = LocalDate.of(12345, 12345, 12345);
//when
LocalDate result = FindSunday.findFirstSunday(date);
//then
assertEquals(LocalDate.of(2024, 9, 29), result);

}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 경우 테스트 자체가 실패할 수 있겠죠.
그렇다면 "실패"를 테스트하시면 됩니다. assertThrows 를 사용하시면 예외가 발생하는 상황을 테스트할 수 있으며, 예외가 발생하지 않으면 테스트를 실패하도록 만들 수 있습니다.

모든 테스트는 "성공" / "예외발생" 을 테스트할 수 있지만 결과적으로 테스트 결과는 모두 "성공" 으로 찍혀야 합니다~ 예외가 발생한다면 예외가 발생해야 성공하는 테스트를 작성하여 모든 테스트가 성공하도록 만들어주세요.


@Order(3)
@Test
@DisplayName("경계테스트 - 최소값 테스트 - success!")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 경우에도 경계테스트라고 작성하기보다는, 명확하게 조건을 작성해주시는게 좋습니다.
"-999999999년을 입력해도 정상적으로 일요일을 찾아 반환한다" 정도로 작성해도 됩니다. 모호한 테스트명만 피해주세요~

public void givenMinLocalDate_whenFindFirstSunday_thenFirstSunday() {
//given
LocalDate date = LocalDate.MIN;
//when
LocalDate result = FindSunday.findFirstSunday(date);
//then
assertEquals(LocalDate.of(-999999999, 01, 7), result);

}


@Order(4)
@Test
@DisplayName("경계테스트 - 최대값 테스트 - fail!")
public void givenMaxLocalDate_whenFindFirstSunday_thenFirstSunday() {
//given
LocalDate date = LocalDate.MAX;
//when
LocalDate result = FindSunday.findFirstSunday(date);
//then
assertEquals(LocalDate.of(999999999, 12, 31), result);

}


/*
@ValueSource 어노테이션을 사용하여 여러개의 값을 테스트에 주입할 수 있다.
특징은 기본형타입만 지원되며 배열형태로 설정이된다 그로고 하나씩 반복실행하게된다
*/
@Order(5)
@ParameterizedTest //@Test 대신 @ParameterizedTest를 사용한다.
@DisplayName("@ValueSource 어노테이션을 사용하여 여러개의 값을 테스트에 주입할 수 있다. - success!")
@ValueSource(strings = {"2024-09-30", "2024-10-01", "2024-10-02", "2024-10-03", "2024-10-04", "2024-10-05"})
Comment on lines +66 to +68
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

명확하게 학습을 위한 테스트로 이름을 지어주셨네요~ 훨씬 테스트의 목적이 명확해보입니다 💯

public void valueSourceTest(String date) {
//given
LocalDate localDate = LocalDate.parse(date);
//when
LocalDate result = FindSunday.findFirstSunday(localDate);
//then
assertEquals(LocalDate.of(2024, 10, 6), result);
}



//@EnumSource 어노테이션은 Enum 타입의 값을 테스트에 주입할 수 있다.
//특정 Enum 타입을 지정하면 해당 Enum의 모든 값을 테스트에 주입한다.
@Order(6)
@ParameterizedTest //@Test 대신 @ParameterizedTest를 사용한다.
@DisplayName("@EnumSource 어노테이션을 사용하여 여러개의 값을 테스트에 주입할 수 있다. - success!")
@EnumSource(Day.class)
//@EnumSource(value = Day.class, names = {"Monday", "Tuesday"}) // Day enum의 Monday, Tuesday만 테스트에 주입
public void enumSourceTest(Day day) {
//given
LocalDate localDate = LocalDate.parse(day.getDate());
//when
LocalDate result = FindSunday.findFirstSunday(localDate);
//then
assertEquals(LocalDate.of(2024, 10, 6), result);

}

/*
@valueSource어노테이션은 기본형만 지원하였으나
@MethodSource어노테이션을 사용하면 복잡한객체나 여러가지 다양한타입의 데이터를 전달가능
데이터를 제공할 메소드는 static으로 선언되어야하며
!!Collection!!을 반환해야한다.
가장 많이사용하는 Stream<T>, Iterable<T>, Iterator<T>, Object[]를 반환해야한다.
그리고 해당 메소드는 테스트클래스나 외부클래스에존재해도된다
*/
@Order(7)
@ParameterizedTest
@DisplayName("MethodSource 이용한 테스트")
@MethodSource("testMethodSource")
public void methodSourceTest(LocalDate date) {
//given
//when
LocalDate result = FindSunday.findFirstSunday(date);
//then
assertEquals(LocalDate.of(2024, 10, 6), result);


}

// test테스트에 사용될 데이터 스트림으로 반환
private static Stream<LocalDate> testMethodSource() {
return Stream.of(LocalDate.of(2024, 9, 30));
}


/*
Csv형태는 String으로 반환하지만
Junit5에서는 자동으로 타입변환을 해준다. 즉 LocalDate로 받아도 된다.
*/
@Order(8)
@ParameterizedTest
@DisplayName("CsvSource 이용한 테스트")
@CsvSource({
"2024-09-30",
"2024-10-01",
"2024-10-02",
"2024-10-03",
"20241004",
"2024/10/05"
})
public void csvSourceTest(LocalDate date) {
//given
//when
LocalDate result = FindSunday.findFirstSunday(date);
//then
assertEquals(LocalDate.of(2024, 10, 6), result);
}



/*
@MethodSource와 다르게 ArgumentsProvider 라는 커스텀데이터 제공 클래스를 통해 데이터를 제공한다
리턴타입은 ArgumentProvider의 ArgumentsStream이다. (Stream<? extends Arguments>)
항상 외부클래스로 구현해야하고 ArgumentsProvider 인터페이스를 반드시 구현해야한다.
이것역시도 자동으로 형변환을 지원해준다
*/
@Order(9)
@ParameterizedTest
@DisplayName("@ArgumentSource 이용한 테스트")
@ArgumentsSource(CustomArgumentsProvider.class)
public void argumentSourceTest(LocalDate date) {
//given
//when
LocalDate result = FindSunday.findFirstSunday(date);
//then
assertEquals(LocalDate.of(2024, 10, 6), result);
}

}
Loading