์ด๋๋ง ํ๋ฆฌ์ฝ์ค์ ๋ง์ง๋ง ๋ฏธ์ ์ด ์ฃผ์ด์ก์ต๋๋ค. ์ฝ์ํ (์ฝ์น์์ ์๋คํ์) ๋ ํฌ๋น ๋๊ณผ ์ฝ์น๋๋ค๊ป์ ๋ง์ํด ์ฃผ์ จ๋ฏ์ด ์ฝ๊ฒ ํ๋ฆฌ์ง๋ ์์์์ต๋๋ค.
๊ฐ์ฒด์งํฅ์ ์๋ฏธ๋ฅผ ๋ค์ ์๊ฐํ์. (ํ์ค ์ธ๊ณ ๋ฐ์ ์ฃผ์)
๊ทธ๋์ ๊ฐ์ฒด์งํฅ์ ์ฌ์ค๊ณผ ์คํด ์ฑ ์ ์ฝ์ผ๋ฉฐ ๋ธ๋ก๊ทธ์๋ ๊ธฐ๋กํ์๋๋ฐ์, ์ฑ ์์๋ ๋ค์๊ณผ ๊ฐ์ ๋ด์ฉ์ด ๋์ต๋๋ค.
๊ฐ์ฒด์งํฅ์ด ๋จ์ํ ํ์ค ์ธ๊ณ์ ๋ชจ๋ฐฉ์ด ์๋ ์ด์ ์ค ํ๋๋ ํ์ค ์์์ ์๋์ ์ธ ์กด์ฌ๊ฐ ๊ฐ์ฒด์งํฅ ์ธ๊ณ์์๋ ๋ฅ๋์ ์ธ ์กด์ฌ๊ฐ ๋ ์ ์๊ธฐ ๋๋ฌธ์ด๋ค. ์ด๋ ๊ฐ์ฒด๋ฅผ ์์ธํ (anthropomorphism) ํ์๊ธฐ ๋๋ฌธ์ด๋ค.
์ฆ, ์ง์ ํ ๊ฐ์ฒด์งํฅ์ ๊ตฌ์ถํ๊ธฐ ์ํด์ ์ฃผ์ํ ์ ์ ํ์ค ์ธ๊ณ๋ฅผ ๊ทธ๋๋ก ์ฎ๊ธฐ๋ ํ์๋ ํ๋ฉด ์ ๋๋ค๋ ๊ฒ์ ๋๋ค.
๊ทธ๋ฌ๋ ์ ๋ ๋ ์ง (Day)์ ์ฃผ๋ฌธ (Orders)๋ฅผ ๋ง๋ ๋ค์, ์ด๋ค์ ์ค์ ๋ก ๊ด๋ฆฌํ๋ Customer (๊ณ ๊ฐ) ๊ฐ์ฒด๋ฅผ ๋ง๋ค๋ฉด ์ด๋จ๊น๋ผ๋ ์๊ฐ์ ํ์์ต๋๋ค. ํ์ง๋ง ์ด๋ ๊ฒ ํ๋ ํ ์ธ ์ ์ฑ ์ ์ ์ฉํ ๋ ๊ฒฐ๊ตญ Customer๋ก๋ถํฐ Day์ Orders๋ฅผ getter ๋ฉ์๋๋ก ํธ์ถํ๋๋ก ํ๊ณ , Customer๊ฐ ์ค์ค๋ก์ ๋ก์ง์ ๊ฐ์ง์ง ๋ชปํ์ต๋๋ค.
์์ฑํ๋ ํ ์ธ ์ ์ฑ ์ Day์ Orders๋ฅผ ์๋ ์ฝ๋์ฒ๋ผ ๋ฐ์ต๋๋ค. ๋ฐ๋ผ์ ๊ฐ์ฒด๊ฐ ๋ ์์จ์ ์ด๋๋ก ํ๊ธฐ ์ํด Customer๋ฅผ ๋ง๋ค์ง ์๊ณ Day์ Orders๋ง ํ์ฉํ๋๋ก ๋ณ๊ฒฝํ์ต๋๋ค.
public interface DiscountPolicy {
int discount(final Day day, final Orders orders);
boolean canDiscount(final Day day, final Orders orders);
}
๋ฐฉ์ด์ ๋ณต์ฌ์ ๋ํ ๊น์ ๊ณ ๋ฏผ
๊ทธ๋์์ ๋ฏธ์ ๋ค์์๋ ์ผ๊ธ ์ปฌ๋ ์ ์ ํ์ฉํ ๋ ๋ฐฉ์ด์ ๋ณต์ฌ๋ฅผ ํ์ฉํ์ง ๋ชปํ์ต๋๋ค. ๊ฐ๋ ์ ๋ํด ์ด๋ฆ๋ง ๋ค์ด๋ณธ ์์ค์ด๊ธฐ๋ ํ๊ณ , ์ดํํฐ๋ธ ์๋ฐ์ ์๋ค๋ ๋ด์ฉ์ด๊ธธ๋ ์ดํด๊ฐ ์ ์ ๋ ๊ฒ ๊ฐ๋ค๋ ์๊ฐ๋ง ํ์๊ธฐ ๋๋ฌธ์ ๋๋ค.
๊ทธ๋ฌ๋ค ์ฐ์ฐํ ์ผ๊ธ ์ปฌ๋ ์ ์์ ์๋ต DTO๋ฅผ ๋ง๋ค๊ธฐ ์ํด getter ๋ฉ์๋๋ฅผ ์ ์ํ๋ค๋ฉด, ๋ด๋ถ์ ์ผ๋ก ๊ฐ์ด ๋ณ๊ฒฝ๋ ์ ์๋ ๊ฒ ์๋๊น?๋ผ๋ ์๋ฌธ์ด ๋ค์์ต๋๋ค. ๊ทธ๋ฆฌ๊ณ ๋ฐ๋ก ์ด ์ ๋๋ฌธ์ ๋์จ ๊ฐ๋ ์ด ๋ฐฉ์ด์ ๋ณต์ฌ์์ ์์์ต๋๋ค.
์์๋ก ๋ก๋ ๋ฏธ์ ์ ๊ฒฝ์ฐ, ๋ฐฉ์ด์ ๋ณต์ฌ๋ฅผ ํ์ง ์๋๋ค๋ฉด ์ปฌ๋ ์ ์ ์์ ๊ฐ์ด ๋ณ๊ฒฝ๋ ์ ์์ต๋๋ค. (getNumbers๋ก ๊ฐ์ ธ์จ ๋ค์์ numbers.add๋ฅผ ์ํค๋ฉด ๋ฉ๋๋ค.)
public class GoalNumbers {
private static final String NUMBER_SPLITTER = ",";
private final List<LottoNumber> numbers;
...
public List<LottoNumber> getNumbers() {
return numbers;
}
}
์ด์ ๋ํด ๋๋ ์ ์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
- ๋ฐฐ์์ ๋ด๊ฐ ํ์ํ๋ค๊ณ ๋๋ ๋ ๊ทธ ํจ๊ณผ๊ฐ ๊ฐ์ฅ ํฌ๋ค.
- ๋ฐฉ์ด์ ๋ณต์ฌ๊ฐ ๋ฌด์กฐ๊ฑด ์ข์ ๊ฒ์ ์๋๋ค. ๊ทธ๋ ๊ธฐ ๋๋ฌธ์ ํ ๋จ์๋ผ๋ฉด ์ถฉ๋ถํ ๋ ผ์๊ฐ ์ด๋ฃจ์ด์ ธ์ผ ํ๋ค.
- ์ผ๊ธ ์ปฌ๋ ์ ์ ๋ถ๋ณ ์์ฑ์ ๋ถ์ด๋ ๊ฒ์ ์ฃผ๋ ๋ชฉ์ ์ด ์๋๋ค.
์ผ๊ธ ์ปฌ๋ ์ ์ ๋ํ ๊ณ ๋ฏผ์ ํ๋ ๊ฒ์ ์ถํ ๋ค๋ฅธ ๊ธ์ ์์ฑํ๋๋ก ํ๊ฒ ์ต๋๋ค. (์ด ๋ด์ฉ ๋ํ ์ฝ๋๋ฅผ ์ง์ ๋ฏ์ด๋ณด๋ฉฐ ํ๊ตฌํ๊ธฐ์..)
๋์๋ JUnit์ ๊ธฐ๋ฅ
3์ฃผ ์ฐจ๊น์ง ์งํํ๋ฉด์ ๋๋ ต๊ธฐ๋ง ํ์๋ JUnit์ ๋ํด ์ด๋ ์ ๋ ์๊ฒ ๋์๋ค๊ณ ์๊ฐํ์๋๋ฐ์, EnumSource, CsvSource ๋ฑ ์ ์ฉํ ๊ฒ๋ค์ ์๊ฒ ๋์์ต๋๋ค. ์ด๋ฐ ๊ฒ์ ๋ณด๋ฉด ๋ฐฐ์์๋ ๋์ด ์๋ ๊ฒ ๊ฐ์ต๋๋ค.
EnumSource
EnumSource๋ Enum์ด ๊ฐ์ง ๊ฐ๋ค๋ก ํ ์คํธ๋ฅผ ๋๋ฆด ๋ ์ ์ฉํ๊ฒ ํ์ฉํ ์ ์์ต๋๋ค.
@ParameterizedTest(name = "{0} ๋ฉ๋ด ๊ธฐ๋ณธ ์์ฑ ์ 1๊ฐ์ฉ ์ ์ฅ๋๋๊ฐ?")
@EnumSource(value = Menu.class)
@DisplayName("๋ฉ๋ด ์ด๋ฆ๋ง์ผ๋ก Order ์์ฑ")
void createOrderWithNameTest(final Menu menu) {
// given
Order order = Order.withMenu(menu);
int expectedSize = 1;
// when
String menuName = order.getName();
int orderSize = order.getSize();
// then
assertAll(
() -> assertThat(menu.isNameSame(menuName)).isTrue(),
() -> assertThat(orderSize).isEqualTo(expectedSize)
);
}
// Menu
public enum Menu {
SOUP(APPETIZER, "์์ก์ด์ํ", 6000),
TAPAS(APPETIZER, "ํํ์ค", 5500),
SALAD(APPETIZER, "์์ ์๋ฌ๋", 8000),
STEAK(MAIN_DISH, "ํฐ๋ณธ์คํ
์ดํฌ", 55_000),
BARBEQUE(MAIN_DISH, "๋ฐ๋นํ๋ฆฝ", 54_000),
SEA_PASTA(MAIN_DISH, "ํด์ฐ๋ฌผํ์คํ", 35_000),
CHRISTMAS_PASTA(MAIN_DISH, "ํฌ๋ฆฌ์ค๋ง์คํ์คํ", 25_000),
CAKE(DESSERT, "์ด์ฝ์ผ์ดํฌ", 15_000),
ICECREAM(DESSERT, "์์ด์คํฌ๋ฆผ", 5000),
COKE(DRINK, "์ ๋ก์ฝ๋ผ", 3000),
WINE(DRINK, "๋ ๋์์ธ", 60_000),
CHAMPAGNE(DRINK, "์ดํ์ธ", 25_000);
...
}
์ ์ฝ๋๋ Menu ํ์ ์ผ๋ก Order๋ฅผ ๋ง๋ค์์ ์, ์ ์์ ์ผ๋ก 1๊ฐ์ฉ ์์ฑ๋๋์ง ๊ฒ์ฆํฉ๋๋ค. ์ฆ SOUP ~ CHAMPAGNE๋ฅผ ํ ๋ฒ์ฉ ๋ฃ์ด๋ณด๋ฉฐ ํ ์คํธํ๋๋ก ํด ์ค๋๋ค.
ParameterizedTest์ name ์์ฑ
ParameterizedTest์ name ์์ฑ์ ๋ถ์ด๋ฉด ์๋์ ๊ฐ์ด ํ ์คํธ๋ฅผ ๋ ์ง๊ด์ ์ผ๋ก ๋๋ฌ๋ผ ์ ์์ต๋๋ค. ์ธ์ ์์น์ ๋ฐ๋ผ {0}, {1}์ ์์ฑํ๋ฉด ๋ฉ๋๋ค.
CsvSource
CsvSource๋ ๋ณต์กํ ๋ฐ์ดํฐ ํํ๋ฅผ ๊ตฌ๋ถํ ์ ์์ต๋๋ค.
private static final String MENU_ORDERS =
"""
์์ก์ด์ํ, 2, SOUP
ํํ์ค, 2, TAPAS
์์ ์๋ฌ๋, 2, SALAD
...
""";
@ParameterizedTest(name = "{0} {1}๊ฐ ์ฃผ๋ฌธ ์์ฑ์ด ๋ฌธ์ ์๋๊ฐ?")
@CsvSource(textBlock = MENU_ORDERS)
@DisplayName("์ ์ Order ์์ฑ")
void validOrderTest(final String nameInput, final String sizeInput) {
// given
String orderInput = nameInput + "-" + sizeInput;
// when & then
assertDoesNotThrow(() -> Order.from(orderInput));
}
์์ ๊ฐ์ด textBlock ํํ๋ก ๋ฐ์ดํฐ๋ฅผ ๋ฐ์ ์ ์์ต๋๋ค. ์ด๋ ๊ธฐ๋ณธ์ ์ผ๋ก๋ ์ฝค๋ง๋ฅผ ๊ธฐ์ค์ผ๋ก ๊ตฌ๋ถ๋ฉ๋๋ค. ๋ํ ์๋ค ๊ณต๋ฐฑ์ ์๋์ผ๋ก ์ ๊ฑฐ๋๋ ๊ฒ ๊ฐ์ต๋๋ค.
์๋๋ textBlock์ ๊ดํ ์ค๋ช ์ ๋๋ค. (delimiter, delimiterString์ผ๋ก ๊ตฌ๋ถ์๋ฅผ ์ ์ํ ์ ์์ต๋๋ค.)
๋๋ ์๋์ ๊ฐ์ ๋ฐฉ๋ฒ๋ ๊ฐ๋ฅํฉ๋๋ค. ์ด ๋ฐฉ๋ฒ์ value๋ฅผ ํ์ฉํฉ๋๋ค.
@ParameterizedTest(name = "{0}์์ด ๋ค์ด์ฌ ๋ {1} Badge์ธ๊ฐ?")
@CsvSource({
"3000, NOT_THING",
"6000, STAR",
"12000, TREE",
"25000, SANTA"
})
@DisplayName("๊ฐ ์
๋ ฅ๊ฐ์ ๋ฐ๋ฅธ ์ ์ ํ Badge ์กฐํ")
void findEachBadgeByCostTest(final int cost, final Badge badge) {
// when
Badge findBadge = Badge.findByCost(cost);
// then
assertThat(findBadge).isEqualTo(badge);
}
assertAll
assertAll์ ์์ EnumSource์์๋ ์์ฑํ๋ฏ, ์ฌ๋ฌ ๊ฐ์ assert๋ฅผ ํจ๊ป ํด์ผ ํ ๋ ์ ์ฉํ ์ ์์ต๋๋ค.
๊ฒฐ๋ก
ํ๋ฆฌ์ฝ์ค๋ฅผ ํ๋ฉด์ ๋์ณค๋ ์ ๋ค์ด ์กฐ๊ธ ์์ด ์์ฝ๊ธฐ๋ ํ์ง๋ง (๋ก๋ ๋ฏธ์ ์์ ๋ณด๋์ค ๋ฒํธ๋ 2๋ฑ๋ง ๊ด์ฌํด์ผ ํ์ง๋ง ๊ทธ๋ฌ์ง ๋ชปํ ์ , static import๋ฅผ ๋ค๋ฅธ ๋ฉ์๋๋ค์๋ ํ ์ ๋ฑ), ์ ์ฒด์ ์ผ๋ก ๋ณด๋ฉด ์ด ๋น์์๋ ํ ์ ์์๋ ์ต์ ์ ํ๋ ๊ฒ ๊ฐ์ต๋๋ค. (๋์น ์ ๋ค์ ๋ํด์๋ ๊ทธ๋๋ ๊ธฐ๋ณธ์ผ๋ก ์ฃผ์ด์ง ํ ์คํธ ๊ฒฐ๊ณผ๋ค์ ๋ชจ๋ ์ฑ๊ณตํ์ผ๋ ๊ด์ฐฎ์ ๊ฒ ๊ฐ๋ค๋ ์์์ ํ๊ณ ์์ต๋๋ค..)
ํ๋ฆฌ์ฝ์ค๋ฅผ 4์ฃผ ๊ฐ ํ๋ฉด์ ์ง์ง ํ๊ต ์์ ์ ๋ค ๋ฒ๋ฆฌ๊ณ , ์์ ์๊ฐ์๋ ํ๋ฆฌ์ฝ์ค๋ง ํ๋ ๋ฑ ์จ์ ํ ๋ชฐ์ ์ ํ๋๋ฐ์. (๋งค์ผ 4์์ ์ ๊ฒ์ ๋น๋ฐ) ๊ทธ๋งํผ ์ข์ ๊ฒฐ๊ณผ๊ฐ ์์ผ๋ฉด ์ข๊ฒ ์ต๋๋ค.
๊ทธ๋ผ์๋ ํ๋ฝ๋ ํ๋ฅ ์ด ํจ์ฌ ๋์ ๊ฒ์ ๋น์ฐํ๋, ์ฐจ์ธฐ ์ฐํ ์ฝ์ ํ๋ฝํ๊ฒ ๋๋ค๋ฉด ์ด๋ป๊ฒ ๋ฌธ์ ์์ด ์ฑ์ฅํ ์ ์์์ง์ ๋ํด์๋ ๊ณ ๋ฏผํด ๋ณผ ๊ณํ์ ๋๋ค.
ํ๋ฆฌ์ฝ์ค ๋ฏธ์ ์ ํ๋ฉด์ ์ฌ๋ฌ ๊ณ ๋ฏผ๋ค์ ํ ์ ์์ด ์ข์๊ณ , ๋ธ๋ก๊ทธ ๊ธ๋ค์ ์ ์ตํ๊ฒ ์ฑ์ธ ์ ์์ด์ ๋๋ฌด ์ข์์ต๋๋ค. ํฉ๋ถ ์ฌ๋ถ๋ฅผ ๋ ๋์ ํ๋ฆฌ์ฝ์ค์ ๋ณธ์ง์ ์ธ ๋ชฉํ๋ ์ด๋ฃจ๊ฒ ๋ ๊ฒ ๊ฐ์ ๋ฟ๋ฏํ๋ค์.
1์ฐจ ์ ๋ฐ๊น์ง ํ ๊ฒ
ํน์ ๋ชจ๋ฅผ 1์ฐจ ์ ๋ฐ ๋์ ํฉ๊ฒฉ์ ๊ธฐ๋ํ๋ฉด์ ํน์ ๋ชจ๋ฅผ ์ต์ข ์ฝ๋ฉํ ์คํธ์ ๋๋นํ๊ธฐ ์ํด, 12.11 (์)๊น์ง ๋จ์ ๊ธฐ๊ฐ ๋์ ์ฐํ ์ฝ์ ๋ค๋ฅธ ๋ฌธ์ ๋ค (์ํ๊ธฐ, ํ์ด๋งค์นญ, ์ ์ฌ ๋ฉ๋ด ์ถ์ฒ, ์งํ์ฒ ๋ ธ์ ๋)์ ํ์ด๋ณด๋ฉฐ ์ฐ์ตํด๋ณด๋ ค ํฉ๋๋ค.
์ค์ ์ฝ๋ฉํ ์คํธ์ฒ๋ผ 5์๊ฐ์ ์ก์ผ๋ฉฐ ์งํํด ๋ณด๊ณ , ํ ์คํธ ์ฝ๋๋ ์๊ฐ์ด ๋จ์ ๊ฒฝ์ฐ์ ์์ฑํ๋ ๊ฒ์ ๋ชฉํ๋ก ํ ๊ณํ์ ๋๋ค. (1์ฐจ ๋ชฉํ๋ ๊ตฌํ์ด ์จ์ ํ ๋๋๋ก, ํ ๋ฉ์๋๊ฐ 15๋ผ์ธ์ ๋์ง ์๋๋ก ๊ด๋ฆฌํ๋ ๊ฒ์ ๋๋ค.)
12์ 11์ผ์ ํฉ๊ฒฉ์ด ๋๋ ๋ถํฉ๊ฒฉ์ด ๋๋ ๋ธ๋ก๊ทธ์ ๊ธฐ๋กํ๊ฒ ์ต๋๋ค!
์ ์ถ ๋งํฌ
์ ์ถ ๋งํฌ๋ ์ฒ์์๋ private ์ ์ฅ์๋ก ์ ํ๋์์ง๋ง ์ด์ public์ผ๋ก ์์ ํด๋ ๋์ด์ ๋งํฌ ๋จ๊ธฐ๊ฒ ์ต๋๋ค!
์ด๊ณณ์์ ํ์ธํ์ค ์ ์์ต๋๋ค.