๋ชฉ์ฐจ
- TDD ๋?
- TDD ๋?
- TDD ์์
- BDD ๋?
- BDD ๋?
- BDD ์์
- TDD vs BDD
- TDD์ BDD์ ํ ํ ์์ฝ
- BDDMockito๋ฅผ ์ด์ฉํ BDD
- ์ค์ต ํ๊ฒฝ ๊ตฌ์ฑ
- Mockito vs BDD Mockito
- Stubbing
- given
- any()
- anyString()
- anyLong()
- willReturn()
- will()
- willThrow()
๋ณธ ์ฃผ์ ๋ฅผ ํ์ตํ๊ธฐ ์ํด์๋ Java์ JUnit5์ BDDMockito ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ์์กด์ฑ์ด ํ์ํฉ๋๋ค.
์ํํธ์จ์ด๋ฅผ ๊ฐ๋ฐํ๋ฉด์ ์ง๊ธ๊น์ง ๋๋ ๊ฐ์ฅ ํฐ ๋ณํ๊ฐ ๋์๊ฒ๋ TDD๊ฐ ์๋๊น ์ถ๋ค.
์ฐ์ฐํ ๊ธฐํ์ TDD๋ฅผ ์ค์ค๋ก ๋์ ํ์์ ๋, ๋ด๊ฐ ๋ค์๋ ์ฒซ ์๊ฐ์
๋๋ฌด ๊ท์ฐฎ์๋ฐ,... ์ด๋ ๊ฒ ํ๋ค๊ฐ ์ธ์ ๋๋ด์ง...? ์ด๊ฑฐ ๊ทธ๋ฅ ํ ์คํธ ์ ์ง๊ณ ๋ฐ๋ก ํ ์ ์๋๋ฐ..
์ด๋ฐ ์๊ฐ์ ๊ฐ๊ณ TDD๋ฅผ ์ฆ์ค ํ๋ ์๊ฐ์ด ์ผ๋ง ์ง๋์ง ์์์ ์คํ๋์ฒ๋ผ ๊ผฌ์ธ ์์ฃผ ๋ชจํธํ ๊ธฐ๋ฅ(Spring Security ์์) ๊ตฌํํด์ผํ ๋๊ฐ ์์๋ค.
๋ ๊ทธ๋ ๋ฏ TDD๋ก ์คํจํ๋ ์ฝ๋๋ฅผ ๋จผ์ ์ง๊ณ ๋ด๊ฐ ๋ญ ํด์ผํ ์ง๋ฅผ ์ ์ด๊ฐ๋ฉด์ ํ๋์ฉ ํ
์คํธ๋ฅผ ์ํํ๊ณ ๊ธฐ๋ฅ์ ๊ณ ์น๋ฉด์ ์ ์ ์คํ๋๊ฐ ํ๋ฆฌ๋ ๊ฒ ๊ฐ์ ๊ธฐ๋ถ์ด ๋ค๊ฒ ๋์๋ค.
๊ทธ๋ฆฌ๊ณ ํ๋ ํ๋ ๊ธฐ๋ฅ์ ์ถ๊ฐํ๋ฉฐ ์ต๊ณ ๋ก ๋ณต์กํ๋ ๊ทธ ๊ธฐ๋ฅ์ ํ๋์ฉ ํ์ด๊ฐ๋ฉฐ ๋๊ผ๋ ๊ฐ์ ์ ์ฆ์ค์์ ๋งน์ ์ผ๋ก ๋ฐ๋๊ฒ ๋ ๊ณ๊ธฐ ๊ฐ์๋ค.
์ด๋ ๋ฏ TDD๋ ์ฌ๋ฌ ๊ฐ๋ฐ์์๊ฒ ์ข์ ๊ฐ๋ฐ ์ต๊ด์ ๊ฐ๊ฒ ํ ์ ์๋ ๋ฐฉ๋ฒ ์ค ๋จ์ฐ ์ต๊ณ ๋ผ๊ณ ์๊ฐํ๋ค.
์ค๋์ TDD์์ ์กฐ๊ธ ๋ ์งํํ BDD์ ๋ํด์ ์์๋ณด๋ ค ํ๋ค.
๊ทธ ์ ์ TDD์ BDD์ ๋ํ ์ฉ์ด ์ ๋ฆฌ๋ฅผ ํด๋ณด์.
TDD, Test Driven Development
TDD๋ Test Driven Development์ผ๋ก ํ ์คํธ๊ฐ ๊ฐ๋ฐ์ ์ฃผ๋ํ๋ค๋ ๊ฐ๋ ์ผ๋ก ์ฌ์ฉ๋๋ค.
TDD๋ ํ ์คํธ๋ฅผ ๋จผ์ ๋ง๋ค๊ณ ํ ์คํธ๋ฅผ ํต๊ณผํ๊ธฐ ์ํ ํ๋๋ค์ด ๋ชจ๋ ๊ฐ๋ฐ์ ์ฃผ๋ํ๋ ๊ฒ์ ๋ชฉํ๋ก ํ๋ค.
๋ณดํต ํ
์คํธ๋ ๊ฐ๋ฐ์ด ๋๋ ํ์ ํ๋ ๊ณผ์ ์ด๋ผ๊ณ ์๊ฐํ ์ ์๋ค.
ํ์ง๋ง TDD๋ ๊ฐ๋ฐ ์ค๊ฐ ์ค๊ฐ์ ๋์ ์๋ ํ
์คํธ๋ฅผ ํตํด ๊ธฐ๋ฅ์ ๊ฐ๋ฐํด์ผ ํ๋ค.
TDD๋ Kent Beck์ด ๊ณ ์ํด๋ธ ๋ฐฉ๋ฒ๋ก ์ผ๋ก ์ด ์ฑ ์ ํ๋์ ๋ฐ์ด๋ธ๋ก ์ฌ๋ฌ ๊ฐ๋ฐ์๋ค์๊ฒ ์ฃผ๋ชฉ๋ฐ๊ณ ์๋๋ฏ ํ๋ค.
TDD๋ฅผ ๋ฒ์ญํ ํ๊ธํ ์ฑ ์ด ์๋๋ฐ, ํน์ ์ด์ ๋ํ ์ ๋ณด๊ฐ ๊ถ๊ธํ๋ค๋ฉด ํด๋น ๋ธ๋ก๊ทธ์ ์๋ ๊ฐ๋ฐ์ ์ฑ ์ฝ๊ธฐ ์ ํ ์คํธ ์ฃผ๋ ๊ฐ๋ฐ - Kent Beck ์ ํ์ธํด ๋ณด๋ ๊ฒ๋ ์ข์ ๋ฐฉ๋ฒ์ธ ๊ฒ ๊ฐ๋ค.
TDD ์์
๊ณ์ฐ๊ธฐ๋ฅผ TDD๋ก ๊ตฌํํด๋ณด์๋ฉด ๋ค์๊ณผ ๊ฐ๋ค.
Step 1. ์ฐ์ ๋น์ด์๋ Calculator ํด๋์ค์ CalculatorTest ํด๋์ค๋ฅผ ์์ฑํ์.
public class Calculator {
}
public class CalculatorTest {
Calculaotr calc = new Calculator();
}
๊ฐ๋จํ๊ฒ ํ
์คํธ ์ฝ๋์ Calculator
ํด๋์ค์ ์ธ์คํด์ค๋ฅผ ์์ฑํด์ฃผ์.
Step 2. ๋ด๊ฐ ์ํ๋ ๊ธฐ๋ฅ(๋ง์
, plus()
)์ด ์ด๋ป๊ฒ ์๊ฒผ๊ณ ๋ญ ๋ฐํํ๋์ง CalculatorTest ์ ์์ฑํ๋ค.
public class Calculator {
}
public class CalculatorTest {
Calculaotr calc = new Calculator();
@Test
void plus() {
int a = 10;
int b = 20;
int result = calc.plus(a, b);
}
}
ํ์ฌ Calculator
ํด๋์ค์๋ plus๋ผ๋ ๋ฉ์๋๊ฐ ์กด์ฌํ์ง ์๊ธฐ ๋๋ฌธ์ ์คํจํ๊ฒ ๋๋ค.
Step 3. Calculator ํด๋์ค์ ํด๋น ๋ฉ์๋๋ฅผ ํต๊ณผํ ์ ์๋ ๊ฐ๋จํ ํด๋์ค๋ฅผ ๊ตฌํํ๋ค.
public class Calculator {
public int plus(int a, int b) {
return 0;
};
}
public class CalculatorTest {
Calculaotr calc = new Calculator();
@Test
void plus() {
int a = 10;
int b = 20;
int result = calc.plus(a, b);
}
}
Calculator์ plus ๋ฉ์๋์์๋ ์ด๋ค ๊ฐ์ด ๋ฐํ๋๋ ์ข๋ค.
๊ทธ๋ฅ ์ผ๋จ ์ปดํ์ผ์ ์ฑ๊ณต์ํค๋ ๊ฒ์ด ๋ชฉ์ ์ด๋ค.
Step 4. Calculator ํด๋์ค์์ ์ด๋ค ๊ฒฐ๊ณผ๋ฅผ ๋ด์ด์ผ ํ ์ง CalculatorTest ํด๋์ค์์ ๋ค์ ์ ์ํ๋ค.
public class Calculator {
public int plus(int a, int b) {
return 0;
};
}
public class CalculatorTest {
Calculaotr calc = new Calculator();
@Test
void plus() {
int a = 10;
int b = 20;
int result = calc.plus(a, b);
assertEquals(result, a + b);
}
}
assertEquals
๋ JUnit 5์ ์กด์ฌํ๋ ๊ฒ์ฆ๋ฌธ์ผ๋ก ๋งค๊ฐ๋ณ์ 2๊ฐ๊ฐ ์๋ก ๋์ผํ๋ฉด ํ
์คํธ ์ฑ๊ณต์ ์๋ฏธํ๋ค.
ํ์ฌ result ์์๋ 0์ ๋ฐํํ๋ ํด๋น ํ ์คํธ๋ ๋ ์คํจํ ๊ฒ์ด๋ค.
Step 5. Calculator ํด๋์ค์์ ํด๋น ํ ์คํธ๋ฅผ ํต๊ณผํ ์ ์๋ ๋ก์ง์ ๊ตฌํํ๋ค.
public class Calculator {
public int plus(int a, int b) {
return a + b;
};
}
public class CalculatorTest {
Calculaotr calc = new Calculator();
@Test
void plus() {
int a = 10;
int b = 20;
int result = calc.plus(a, b);
assertEquals(result, a + b);
}
}
BDD, Behavior Driven Development
BDD๋ ์ ํ ์๋ก์ด ๋ฐฉ๋ฒ์ด ์๋๋ค.
Danial Terhorst-North์ Charis Matts๊ฐ ์ฐฉ์ BDD Introducing - Dan north & associates์ ๋ฐฉ๋ฒ๋ก ์ผ๋ก BDD์ ๋ชจ๋ ๊ทผ๊ฐ์ TDD์์ ์ฐฉ์๋์๊ธฐ ๋๋ฌธ์ TDD๊ฐ ์ถ๊ตฌํ๋ ๊ฐ์น์ ํฌ๊ฒ ๋ค๋ฅด์ง ์๋ค.
Danial Terhorst-North๊ฐ ์ฒ์์ผ๋ก BDD๋ฅผ ์๊ฐํด ๋ธ ๋๋ ๋ฐ๋ก TDD๋ฅผ ์ํํ๊ณ ์๋ ๋์ค์ด์๋ค๊ณ ํ๋ค.
The deeper I got into TDD, the more I felt that my own journey had been less of a wax-on, wax-off process of gradual mastery than a series of blind alleys. I remember thinking “If only someone had told me that!” far more often than I thought “Wow, a door has opened.” I decided it must be possible to present TDD in a way that gets straight to the good stuff and avoids all the pitfalls.
My response is behaviour-driven development (BDD). It has evolved out of established agile practices and is designed to make them more accessible and effective for teams new to agile software delivery. Over time, BDD has grown to encompass the wider picture of agile analysis and automated acceptance testing
์ด ๋ง์ ๊ฐ๋จํ๊ฒ ํ์๋ฉด TDD๋ฅผ ํ๋ค๊ฐ ํด๋น ์ฝ๋๋ฅผ ๋ถ์ํ๊ธฐ ์ํด์ ๋ง์ ์ฝ๋๋ค์ ๋ถ์ํด์ผํ๊ณ ๋ณต์ก์ฑ์ผ๋ก ์ธํด '๋๊ตฐ๊ฐ๊ฐ ๋์๊ฒ ์ด ์ฝ๋๋ ์ด๋ค์์ผ๋ก ์ง์ฌ์ก์ด! ๋ผ๊ณ ๋ง์ ํด์คฌ์ผ๋ฉด ์ข์์ ํ ๋ฐ' ๋ผ๋ ์๊ฐ์ ํ๋ค๊ฐ ๋ณด๋ ํ๋ ์ค์ฌ ๊ฐ๋ฐ์ ํ๋ฉด ์ข๊ฒ ๋ค๊ณ ์๊ฐํ๋ค.
BDD๋ ํ๋์ ๊ธฐ๋ฐํ์ฌ TDD๋ฅผ ์ํํ์๋ ๊ณตํต์ ์ดํด์ธ๋ฐ, ์ด๋ฅผ ํ ๋ฌธ์ฅ์ผ๋ก ๋ง ํ์๋ฉด ๋ค์๊ณผ ๊ฐ๋ค.
BDD๋ ์ ํ๋ฆฌ์ผ์ด์ ์ด ์ด๋ป๊ฒ ํ๋ํด์ผ ํ๋์ง์ ๋ํ ๊ณตํต๋ ์ดํด๋ฅผ ๊ตฌ์ฑํ๋ ๋ฐฉ๋ฒ์ด๋ค.
BDD์ ํ๋
BDD ์์๋ ํ๋์ ๋ํ ์คํ์ ๋ค์๊ณผ ๊ฐ๋ค.
- Narrative
- Given / When / Then
๋ชจ๋ ํ ์คํธ ๋ฌธ์ฅ์ Narrative ํ๊ฒ ๋์ด์ผ ํ๋ค. ์ฆ, ์ฝ๋๋ณด๋ค ์ธ๊ฐ์ ์ธ์ด์ ์ ์ฌํ๊ฒ ๊ตฌ์ฑ๋์ด์ผ ํ๋ค.
TDD ์์๋ ์ฌ์ค์ ํ ์คํธ ์ฝ๋๋ฅผ ์ด์ฉํ ๊ตฌํ์ ์ด์ ์ด ๋ง์ถฐ์ ์๋ค.
ํ์ง๋ง BDD๋ ๊ทธ๋ ์ง ์๋ค.
BDD๋ TDD๋ฅผ ์ํํ๋ ค๋ ์ด๋ ํ ํ๋๊ณผ ๊ธฐ๋ฅ์ ๊ฐ๋ฐ์๊ฐ ๋ ์ดํดํ๊ธฐ ์ฝ๊ฒํ๋ ๊ฒ์ด ๋ชฉ์ ์ด๋ค.
๋ชจ๋ ํ ์คํธ ๋ฌธ์ฅ์ Given / When / Then ์ผ๋ก ๋๋ ์ ์์ฑํ ์ ์์ด์ผ ํ๋ค.
์์์ ๋ง ํ Narrative๋ฅผ ์ํํ๊ธฐ ์ํ ์ค์ง์ ์ธ ๊ฐ๋ ์ด๋ผ๊ณ ์๊ฐํ๋ฉด ๋ ๊ฒ ๊ฐ๋ค.
- Given
- ํ ์คํธ๋ฅผ ์ํด ์ฃผ์ด์ง ์ํ
- ํ ์คํธ ๋์์๊ฒ ์ฃผ์ด์ง ์กฐ๊ฑด
- ํ ์คํธ๊ฐ ๋์ํ๊ธฐ ์ํด ์ฃผ์ด์ง ํ๊ฒฝ
- When
- ํ ์คํธ ๋์์๊ฒ ๊ฐํด์ง ์ด๋ ํ ์ํ
- ํ ์คํธ ๋์์๊ฒ ์ฃผ์ด์ง ์ด๋ ํ ์กฐ๊ฑด
- ํ ์คํธ ๋์์ ์ํ๋ฅผ ๋ณ๊ฒฝ์ํค๊ธฐ ์ํ ํ๊ฒฝ
- Then
- ์์ ๊ณผ์ ์ ๊ฒฐ๊ณผ
์ฆ, ์ด๋ค ์ํ์์ ์ถ๋ฐ (given) ํ์ฌ ์ด๋ค ์ํ์ ๋ณํ๋ฅผ ๊ฐํ์ ๋ (when) ๊ธฐ๋ํ๋ ์ด๋ ํ ์ํ๊ฐ ๋์ด์ผ ํ๋ค.(then)
BDD ์์
์์ ์ํฉ์์ TDD์ ํ๋ฆ์ ๋นผ๊ณ BDD์ ํต์ฌ ๊ฐ๋ ์ธ ํ๋์ ๋ฐ๋ฅธ Given When Then ์ ๋์ ํ๋ฉด ๋ค์๊ณผ ๊ฐ๋ค.
public class Calculator {
public int plus(int a, int b) {
return a + b;
};
}
public class CalculatorTest {
Calculaotr calc = new Calculator();
@Test
void plus() {
// given
int a = 10;
int b = 20;
// when
int result = calc.plus(a, b);
// then
assertEquals(result, a + b);
}
}
ํฐ ์ฐจ์ด๊ฐ ์์ง ์๋?
๋น์ฐํ ๊ฒ์ด๋ค. BDD ์์ฒด๊ฐ TDD์์ ๋ ์๋ก์ด ๊ฐ๋ ์ด ์๋๋ผ TDD๋ฅผ ๋ ์, ๋ ๋ฉ์ง๊ฒ, ๋ ํ์กฐ์ ์ผ๋ก ์ฌ์ฉํ๊ธฐ ์ํ ๋ฐฉ๋ฒ์ด๊ธฐ ๋๋ฌธ์ด๋ค.
TDD vs BDD
์์ ๋ง ํ์ง๋ง TDD์ BDD๋ฅผ ๋น๊ตํ๋ค๋ ๊ฒ์ ์ฝ๊ฐ ๋ค๋ฅธ ์ฃผ์ ์ ์ด์ผ๊ธฐ๋ผ ๋น๊ต ํ๋ค๋ ๊ฒ์ด ํ๋ค๊ฒ ๊ฐ์ง๋ง ๊ฐ์ธ์ ์ผ๋ก ๋น๊ต๋ฅผ ํด๋ณด์๋ฉด ๋ค์๊ณผ ๊ฐ์ ํ๊ฐ ๋์ฌ ๊ฒ ๊ฐ๋ค.
์ด๋ฆ | ์ฐฝ์์ | Based | ํต์ฌ | ๋ผ์ด๋ธ๋ฌ๋ฆฌ |
TDD | Kent-Beck | ์๋๋ฆฌ์ค ๊ธฐ๋ฐ | ํ ์คํธ๊ฐ ์ฃผ๋ํ๋ ๊ฐ๋ฐ | JUnit5, Mockito |
BDD | Dan North | ํ๋ ๊ธฐ๋ฐ | ์์ฐ์ด์ ๋ ๊ฐ๊น๊ฒ TDD | JUnit5, BDDMockito |
BDDMockito๋ฅผ ์ด์ฉํ BDD
BDD๋ฅผ ์คํํ๊ธฐ ์ํด์๋ Given / When / Then ๊ตฌ์กฐ๋ฅผ ์ ์ฌ์ฉํด์ผ ํ๋ค.
์์ ๋ฐฉ๋ฒ์ฒ๋ผ ์ฃผ์์ ์ด์ฉํ์ฌ ๊ตฌ๋ถํ๋ ๊ฒ๋ BDD์ด์ง๋ง ์ด๋ณด๋ค ๋ BDD ์ค๋ฝ๊ฒ BDD๋ฅผ ์ค์ฒํ ์ ์๋ ๋ฐฉ๋ฒ์ด ๋ฐ๋ก BDDMockito๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ด๋ค.
BDDMockito๋ Mockito ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๋ด์ ์กด์ฌํ๋ BDDMockito ํด๋์ค๋ฅผ ๋ง ํ๋ฉฐ BDD ์งํฅ์ ์ธ ๊ฐ๋ฐ์ mockito ์์ api ํํ๋ก ์ ๊ณตํ๋ค.
์ค์ต ํ๊ฒฝ ๊ตฌ์ฑ, Dependencies
Springboot ํ๋ก์ ํธ๋ฅผ ์์ฑํ๋ฉด ์๋์ผ๋ก JUnit-jupiter ์์กด์ฑ์ด ์ถ๊ฐ๋๋๋ฐ ํด๋น ์์กด์ฑ์ด ์ถ๊ฐ๋๋ฉฐ ํจ๊ป mockito ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ์ถ๊ฐ๋๋ค.
maven
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.10.19</version>
<scope>test</scope>
</dependency>
gradle
testImplementation group: 'org.mockito', name: 'mockito-all', version: '1.10.19'
Mockito vs BDDMockito
์์ Mockito์์ BDD์ Given / When / Then ์ ์ํด์ ๋ค์๊ณผ ๊ฐ์ด when(obj)
๋ฉ์๋์ thenReturn()
์ด์ฉํ๊ณ verity()
๊ตฌ๋ฌธ์ ์ด์ฉํด ๊ฒ์ฆํ๋ค.
์ด๋ฐ์์ผ๋ก ํน์ ์ํฉ์ ๋ํ (when๊ณผ ๊ฐ์ด) ์ฐ๋ฆฌ๊ฐ ๊ฐ์ง๋ก ๊ฒฐ๊ณผ๋ฅผ ๋ง๋ค์ด ์ฃผ๋ ๊ฒ์ Stubbing(์คํฐ๋น) ์ด๋ผ๊ณ ํ๋ค. ์ฆ ๊ฐ์ง๋ก ์ํํ ๊ฐ์ฒด๋ฅผ ๋ฃ์ด์ฃผ๋ ๊ฒ์ด๋ค.
when(phoneBookRepository.contains(momContactName))
.thenReturn(false);
phoneBookService.register(momContactName, momPhoneNumber);
verify(phoneBookRepository)
.insert(momContactName, momPhoneNumber);
์ด๊ฒ ์ผ๋ จ์ BDD์ Stubbing ๊ณผ์ ์ธ๋ฐ, ๋ญ๊ฐ ๋ง์ง ์๋ ๋ถ๋ถ์ด ์๋ค.
- when()
- thenReturn()
- verity()
์์ ์กด์ฌํ๋ 3๊ฐ์ง ํ๋ ๊ณผ์ ์ธ๋ฐ, ๋ญ๊ฐ ์ด์ํ๋ค.
๊ฐ๋
์ ์ผ๋ก given ์ ํด๋น๋๋ Mockito์ when(phoneBookRepository.contains(momContactName))
์ ์ด๋ฆ์ด when์ด๋ผ ์ฝ๊ฒ ํ๊ฐ๋ฆด ์ ์๊ฒ ๋์ด์๋ค.
์ด๋ฌํ ๋ฌธ์ ์ ์ BDDMockito์ given() ๋ฉ์๋๋ฅผ ์ด์ฉํด์ ํด๊ฒฐํ ์ ์๋ค.
BBDMockito๋ฅผ ์ด์ฉํ๋ฉด Mockito์ when()
์ given()
์ด๋ผ๋ ๋ฉ์๋๋ก ๋ ์ ํํ ์๋ฏธ ์ ๋ฌ์ ํ ์ ์๋ค.
given(someClass.method()).willReturn()
given() ๋ฉ์๋
์ฐ์ given() ๋ฉ์๋๋ BDD์ givne์ ๊ทธ๋๋ก ๋ดํฌํ๊ณ ์๋ค.
์ด๋ฐ givne()์ ํ๋ผ๋ฏธํฐ๋ฅผ ์ด์ฉํด์ ์ด๋ค ์ํฉ์ ์ฆ, ์ด๋ค ๋ฉ์๋๊ฐ ์คํ๋์์ ๋์ ํ ์คํธ๋ฅผ ์ํ ์ํฉ์ ์ค์ ํ ์ ์๋ค.
๋ค์๊ณผ ๊ฐ์ ์ํฉ์ ๋ฉ์๋๊ฐ ์๋ค๊ณ ๊ฐ์ ํด๋ณด์.
public interface MemberRepository extends JpaRepository<Member, Long> {
boolean existsByEmail(String email);
}
public class MemberService {
@Autowired
private MemberRepository memberRepository;
public boolean isExistEmail(String email) {
return memberRepository.existsByEmail(email); // ์กด์ฌํ๋ฉด true, ์กด์ฌํ์ง ์์ผ๋ฉด false ๋ฐํ
}
}
๊ทธ๋ผ ์๋์ ๊ฐ์ด ํ ์คํธ ์ฝ๋๋ฅผ ๊ตฌ์ฑํ ์ ์๋ค.
public interface MemberRepository extends JpaRepository<Member, Long> {
boolean existsByEmail(String email);
}
public class MemberService {
@Autowired
private MemberRepository memberRepository;
public boolean isExistEmail(String email) {
return memberRepository.existsByEmail(email); // ์กด์ฌํ๋ฉด true, ์กด์ฌํ์ง ์์ผ๋ฉด false ๋ฐํ
}
}
public class MemberServiceTest {
@MockBean
private MemberService memberService;
private final MemberRepository memberRepository = mock(MemberRepository.class);
@BeforeEach
void stubbing() {
given(memberRepository.existsByEmail(any(String.class)))
.willReturn(false);
}
}
๊ทธ๋ผ ๋ ์์ธํ ์์๋ณด์.
์์ Stubbing ๋ฌธ์ฅ์์ 3๊ฐ์ง์ ์๋ฏธ๋ฅผ ๋ด๊ณ ์๋ค.
- Mocking ํ ๋ฉ์๋
- ํด๋น ๋ฉ์๋์ ํ๋ผ๋ฏธํฐ
- ํด๋น ๋ฉ์๋๋ฅผ ์ํํ์ ๋ ๋ฐํํ๋ ๊ฐ
Mocking ํ ๋ฉ์๋
Mocking์ ํ ๋ฉ์๋๋ ๋ญ๊น?
์ฐ๋ฆฌ๋ ์ง๊ธ MemberService๋ฅผ ํ ์คํธ๋ฅผ ํ๋ คํ๋ค.
Unit Test์์ ์ค์ํ ๊ฒ์ ํ ์คํธํ๋ ค๋ ๋์์ ๊ณ ๋ฆฝ์ด๋ค.
ํ ์คํธ ๋์์ ๊ณ ๋ฆฝํ๋ค๋ ๋ป์, ํ ์คํธ ๋์์ ์ฐ๊ด๋ ๋ค๋ฅธ ๊ฐ์ฒด๋ค์ ๊ด์ฌํ์ง ์๋๋ก ์ฐ๋ฆฌ๊ฐ ๊ฐ์ง ๊ฐ์ฒด๋ฅผ ๋ฃ์ด์ค์ผ ํ๋ค๋ ์๋ฆฌ์ ๋น์ทํ๋ค๊ณ ํ ์ ์๋ค.
์ด๋ ๊ฒ ํ
์คํธ ๋์์ ๊ณ ๋ฆฝํ๊ธฐ ์ํด์ Mockito์ mock()
๋ฅผ ์ด์ฉํ์๊ณ , ํ
์คํธ ๋์์ด ํน์ ๊ฒฐ๊ณผ๋ฅผ ์ํํ๊ธฐ ์ํด ์ฐ๊ด๋ ๊ฐ์ฒด์ ์ฐ์ฐ์ ์ฃผ์
ํด์ฃผ๋ฉด ๋๋ค.
MemberService
์๋ ํ์ฌ isExistByEmail ๋ฉ์๋๊ฐ ์กด์ฌํ๊ณ ํด๋น ๋ฉ์๋ ๋ด๋ถ์ MemberRepository
์ธ์คํด์ค๊ฐ existsByEmail ์ฐ์ฐ์ ์ํํ๊ณ ์์ผ๋ฏ๋ก ์ฐ๋ฆฌ๋ MemberRepository์ existsByEmail ์ ๊ฐ์ง๋ก ์ฃผ์
ํด์ฃผ๋ฉด ๋๋ค.
ํด๋น ๋ฉ์๋์ ํ๋ผ๋ฏธํฐ
ํด๋น ๋ฉ์๋, ์ฆ existsByEmail ์ด๋ผ๋ ๋ฉ์๋๋ String ์ผ๋ก email์ ๋ฐ๊ณ ์๋ค.
๊ทธ๋ผ ์ฐ๋ฆฌ์๊ฒ ์ฌ๋ฌ ์ ํ์ง๊ฐ ์กด์ฌํ๋ค.
- ๋ชจ๋ ๊ฐ์ ๋ฐ์์ ๋์ ํ๋ ์ ์ํ๊ธฐ
- ํน์ ๊ฐ์ ๋ฐ์์ ๋์ ํ๋ ์ ์ํ๊ธฐ
๋ชจ๋ ๊ฐ์ ๋ฐ์์ ๋์ ํ๋ ์ ์ํ๊ธฐ, any()
๋ชจ๋ ๊ฐ์ฒด๊ฐ ๋ค์ด์์ ๋์ ํ๋์ any(Object object)
๋ฅผ ์ด์ฉํ ์ ์๋ค.
์ฌ๊ธฐ์๋ String ์ธ ๋ชจ๋ ๊ฐ์ฒด๊ฐ ๊ฐ๋ฅํ๋ค๋ ์๋ฆฌ๋ค.
์์ ๊ฐ์ ์ํฉ์ (์ด๋ ํ String ๊ฐ์ด๋ผ๋ ๊ฐ๋ฅํ ์ํฉ) ์ฌ์ฉํ ์ ์๋ ๋ฉ์๋๊ฐ ์ฌ๋ฟ ์กด์ฌํ๋ค
- anyString() : ์ด๋ ํ ๋ฌธ์์ด
- anyLong() : ์ด๋ ํ Long
- anyBoolean() : ์ด๋ ํ Boolean ๊ฐ
- any() : ๋ชจ๋ ๊ฐ์ฒด
public class MemberServiceTest {
// ... ์๋ต
@BeforeEach
void stubbing() {
given(memberRepository.existsByEmail(anyString()));
given(memberRepository.findById(anyLong()));
given(memberRepository.findAllByOffline(anyBoolean()))
.willReturn(false);
}
}
ํน์ ๊ฐ์ ๋ฐ์์ ๋์ ํ๋ ์ ์ํ๊ธฐ, eq()
์์ ์ํฉ์์๋ ๋ชจ๋ String์ ๋ฐ์์ ๋์ ํ๋์ ์ ์ํ๋๋ฐ, ๋ง์ฝ ํน์ ํ String ๊ฐ์ด ๋ค์ด์์ผ ํ๋ค๋ฉด?
ํน์ ํน์ ํ ๊ฐ์ฒด๊ฐ ๋ค์ด์์ผ ํ๋ค๋ฉด?
๊ทธ๋ด ๋ ์ฌ์ฉํ๋๊ฒ ๋ฐ๋ก eq()
๋ฉ์๋์ด๋ค.
- eq() : ๋์ผํ ๊ฐ ํน์ ๊ฐ์ฒด์ ํ๋
ํด๋น ๋ฉ์๋๋ฅผ ์ํํ์ ๋ ๋ฐํํ๋ ๊ฐ
์์ ๊ฐ์ด ๋ค์ํ ๋ฐฉ๋ฒ์ผ๋ก ํ๋์ ์ ์ํ์๋ค.
๊ทธ๋ผ ์ด์ ํด๋น ํ๋์ ์ ์ ํ ๋ฐํ์ ํด์ค์ผ ํ๋ค.
ํ๋์ ๋ฐํํ ๋๋ ํฌ๊ฒ 3๊ฐ์ง ๋ฐฉ๋ฒ์ด ์กด์ฌํ๋ค.
- willReturn()
- will()
- willThrow()
willReturn()
๋ป ๊ทธ๋๋ก ๋ฐํ ๊ฐ์ ๋ช ์ํด์ค ์ ์๋ค.
public class MemberServiceTest {
// ... ์๋ต
@BeforeEach
void stubbing() {
given(memberRepository.existsByEmail(any(String.class)))
.willReturn(false);
}
@Test
void exists_valid() {
boolean isExist = memberService.isExistEmail("valid@gmail.com");
assertTrue(isExist);
}
}
will() + invocation
will()์ willReturn๊ณผ๋ ์กฐ๊ธ ๋ค๋ฅด๋ค.
willReturn์ ๊ณ ์ ๋ ๊ฐ์ ๋ฐํํ๋๋ฐ, will() ์์๋ invocation ์ ํตํด์ ์๋ก์ด ๊ฐ์ฒด๋ฅผ ๋ฐํํ๊ฑฐ๋ ์์ ์๋ก์ด ํ๋์ ๋ฐํํ ์ ์๋ค.
public class MemberServiceTest {
// ... ์๋ต
@BeforeEach
void stubbing() {
given(memberRepository.findById(anyLong()))
.will(invocation -> {
Member member = invocatoin.getArgument(0);
return MemberData.builder()
.email("test123@gmail.com")
.isFound(true)
.build();
});
}
@Test
void exists_valid() {
MemberData memberData = memberService.findMember(1L);
assertNotNull(memberData);
}
}
willThrow()
willThrow๋ ๋ง ๊ทธ๋๋ก ์์ธ๋ฅผ ๋์ง๋ค.
public class MemberServiceTest {
@BeforeEach
void stubbing() {
given(memberRepository.existsByEmail(eq("hello@gmail.com")))
.willThrow(MemberDuplicationException("exists");
}
@Test
void exists_valid() {
MemberDuplicationException exeption = assertThrow(
MemberDuplicationException.class,
() -> memberService.isExistEmail("valid@gmail.com")
);
assertEquals(exception.getMessage(), "exists");
}
}
๋๊ธ