๐ Mocking์ด๋?
- Mocking์ ํ ์คํธ ์ฝ๋๋ฅผ ์์ฑํ ๋, ์ค์ ๊ฐ์ฒด๋ ์ธ๋ถ ์์กด์ฑ์ ํ๋ด ๋ด๋(Mock) ๊ฐ์ง ๊ฐ์ฒด๋ฅผ ๋ง๋ค์ด ์ฌ์ฉํ๋ ๊ฒ
- ์ฝ๊ฒ ๋งํด, ์ค์ ์๋น์ค๋ฅผ ๋์ฒดํ ์ ์๋ ๊ฐ์ง ์๋น์ค๋ฅผ ๋ง๋ค๊ณ , ๊ทธ๊ฑธ ์ด์ฉํด ํ ์คํธ๋ฅผ ์งํํ๋ ๊ธฐ๋ฒ์ด๋ค.
โ์ Mocking์ด ํ์ํ๊ฐ?
1. ์ธ๋ถ ์์์ ์์กด์ ์ธ ๋ก์ง ํ ์คํธ๋ฅผ ๊ฐํธํ๊ฒ ๋ง๋ค๊ธฐ ์ํด
Spring ์ ํ๋ฆฌ์ผ์ด์ ์์ ์๋น์ค๋ฅผ ํ ์คํธํ๋ ค๊ณ ํ ๋ ๋ณดํต DB๋ ์ธ๋ถ API๋ฅผ ํธ์ถํ๋ ๊ฒฝ์ฐ๊ฐ ๋ง๋ค.
- ๋ง์ฝ ์ค์ DB๋ ์ธ๋ถ API๋ฅผ ๋งค๋ฒ ์ฐ๊ฒฐํด์ผ ํ๋ค๋ฉด, ํ ์คํธ ํ๊ฒฝ ์ธํ ์ด ๋ฒ๊ฑฐ๋กญ๊ณ , ๋คํธ์ํฌ ๋ฌธ์ ๋ DB ์ ์ ๋ฌธ์ ๋ค์ผ๋ก ์ธํด ํ ์คํธ๊ฐ ์คํจํ๊ฑฐ๋, ๋๋ ค์ง ์ ์๋ค.
- ์ด๋ฐ ์ํฉ์์ ๊ฐ์ง๋ก DB๋ฅผ ํ๋ด ๋ด๋ Mock ๊ฐ์ฒด๋ฅผ ์ฌ์ฉํ๋ฉด, DB ์ฐ๊ฒฐ ์์ด๋ 'DB๊ฐ ์๋ค๊ณ ๊ฐ์ 'ํ๊ณ ํ ์คํธ๋ฅผ ์งํผ ํ ์ ์๋ค.
2. ๋ ๋ฆฝ์ ์ด๊ณ ๋น ๋ฅธ ํ ์คํธ
ํ ์คํธ์ฝ๋๋ ์์ ๋จ์(๋จ์ํ ์คํธ, ์ ๋ํ ์คํธ)๋ก ๋น ๋ฅด๊ฒ ๋์์ผ ์๋ฏธ๊ฐ ์๋ค.
- ํ์ง๋ง, ๋ชจ๋ ์์กด ๊ฐ์ฒด๋ฅผ ์ค์ ๋ก ์ฐ๊ฒฐํ๋ฉด ์๋๊ฐ ๋๋ ค์ง๊ณ ๋ค๋ฅธ ์์๋ค(๋คํธ์ํฌ๋ DB ์ํ)์ ์์กด์ ์ด ๋์ด ํ ์คํธ ๊ฒฐ๊ณผ๊ฐ ์ผ๊ด์ ์ด์ง ์๊ฒ ๋๋ค.
- Mocking์ ํตํด ์๋น์ค์ ํต์ฌ ๋ก์ง์ ๋ ๋ฆฝ์ ์ผ๋ก ๊ฒ์ฆํ ์ ์๊ณ , ํ ์คํธ ์๋๋ ๋งค์ฐ ๋นจ๋ผ์ ธ ์ ๋ขฐ๋๊ฐ ๋์์ง๋ค.
3. ๋ณต์กํ ๋ก์ง์ ๋จ์ํํ๊ณ ์๋ฌ๋ฅผ ์ผ์ฐ ๋ฐ๊ฒฌ ๊ฐ๋ฅ
- ํ ์คํธํ ๋ ์ธ๋ถ ๋ก์ง์ ๋จ์ํํจ์ผ๋ก์จ, ๋ด ์ฝ๋๊ฐ ์๋๋๋ก ๋์ํ๋์ง ๋น ๋ฅด๊ฒ ํ์ธ ํ ์ ์๋ค.
- ์๋ฌ๊ฐ ๋ฐ์ํ๋ฉด ๋ฌธ์ ๋ฅผ ๋ค๋ฅธ ๊ณณ(DB ์ค์ ์ค๋ฅ ๋ฑ)์์ ์ฐพ๊ธฐ ๋ณด๋ค๋ ํด๋น ๋ฉ์๋์ ๋ก์ง์์ ๊ฒ์ฆ์ด ๊ฐ๋ฅํ๋ค.
โญ๏ธ Test์์ Mocking์ด ์ค์ํ ์ด์ ๋?
1. ๋จ์ ํ ์คํธ(์ ๋ ํ ์คํธ)์ ๋ ๋ฆฝ์ฑ ๋ณด์ฅ
- ์ค์ DB, ๋คํธ์ํฌ, ํ์ผ ์์คํ ๋ฑ ์ธ๋ถ ์์กด์ฑ๊ณผ ๋ถ๋ฆฌํจ์ผ๋ก์จ ํ ์คํธ ์คํ ์๋๊ฐ ๋น ๋ฅด๊ณ , ํ ์คํธ ๊ฒฐ๊ณผ๋ฅผ ์์ ์ ์ผ๋ก ์ฌํํ ์ ์๋ค.
→ ๋คํธ์ํฌ ์ํ๋ DB ์ฐ๊ฒฐ์ ์์กดํ์ง ์๊ณ ๋ ํ ์คํธ๊ฐ ํญ์ ๋์ผํ ๊ฒฐ๊ณผ๋ฅผ ๋ด๋๋ก ํ๋ค.
2. ํ ์คํธ ๋ฒ์์ ์ ํ์ฑ ํฅ์
- ์ํ๋ ์๋๋ฆฌ์ค(์ฑ๊ณต์ผ์ด์ค, ์์ธ์ผ์ด์ค)๋ฅผ ๊ตฌ์ฒด์ ์ผ๋ก ์ค์ ํ๊ณ , ํด๋น ์๋๋ฆฌ์ค์ ๋ํ ์๋ต์ Mock ๊ฐ์ฒด์์ ์ ์ดํ ์ ์์ด ๋ค์ํ ์ผ์ด์ค๋ฅผ ํ ์คํธํ๊ธฐ ์ฉ์ดํ๋ค.
→ ์ฌ์ฉ์ ์กฐํ ์ DB๊ฐ ์๋ฌ๋ฅผ ๋ฐํฉ ์ด๋ผ๋ ์ผ์ด์ค๋ฅผ ์ค์ DB ์ธํ ์์ด๋ Mock์ผ๋ก ์๋ฎฌ๋ ์ด์ ์ด ๊ฐ๋ฅํ๋ค.
3. ํ ์คํธ ์์ฑ ๋ฐ ์ ์ง๋ณด์ ์ฉ์ด
- Mock์ ์ฌ์ฉํด ๋ณต์กํ ์์กด์ฑ์ ๊ฐ๋จํ๊ฒ ๋์ฒดํ๋ฉด ํ ์คํธ์ฝ๋๊ฐ ๊ฐ๋ฒผ์์ง๊ณ ์ง๊ด์ ์ผ๋ก ๋๋ค.
- ์๋น์ค ๋ก์ง ๋ณ๊ฒฝ ์์๋, ์ค์ ์ธํ๋ผ ์ค์ ์ ๋ณ๊ฒฝํ ํ์ ์์ด Mock ์๋ต๋ง ์์ ํ๋ฉด ๋๋ฏ๋ก ์ ์ง๋ณด์๊ฐ ์ฝ๋ค.
4. ๋น ๋ฅธ ํผ๋๋ฐฑ ๋ฃจํ
- TDD๋ BDD ์๋๋ฆฌ์ค์์ Mock์ ์ฌ์ฉํ๋ฉด, ์ ํ๋ฆฌ์ผ์ด์ ์ ์ฒด๋ฅผ ๊ตฌ๋ํ์ง ์๊ณ ๋ ํน์ ๊ณ์ธต(Controller, Service)๋ง ๋น ๋ฅด๊ฒ ํ ์คํธํด๋ณผ ์ ์๋ค.
- ๋น๋ ํ์ดํ๋ผ์ธ์์๋ ๋จ์ ํ ์คํธ๋ ๊ฐ์ฅ ๋จผ์ ์ํ๋๋ฉฐ, ๋น ๋ฅด๊ฒ ์คํจ ๋๋ ์ฑ๊ณต ์ฌ๋ถ๋ฅผ ์๋ ค์ค๋ค.
5. ๊ฒฐํฉ๋(Coupling) ๋ฎ์ถ๊ธฐ
- Mocking์ ๊ฐ์ฒด ๊ฐ ๊ฒฐํฉ์ ๋ฎ์ถ๊ณ , ์ธํฐํ์ด์ค ๊ธฐ๋ฐ ์ค๊ณ๋ฅผ ๋ ๋ คํ๋ค.
- SOLID ์์น ์ค DIP(Dependency Inversion Principle)๋ฅผ ์งํฌ ์ ์๋๋ก ๋์์ค๋ค.
6. ๋น์ฆ๋์ค ๋ก์ง ๊ฒ์ฆ์ ์ง์ค
- ์ธ๋ถ ์์คํ /๋ผ์ด๋ธ๋ฌ๋ฆฌ ๋์๊น์ง ๊ณ ๋ คํ์ง ์๊ณ , ์์ํ๊ฒ ์ฐ๋ฆฌ๊ฐ ์์ฑํ ์ฝ๋์ ๋ก์ง๋ง ๊ฒ์ฆํ ์ ์์ผ๋ฏ๋ก "๋ฌด์์ด" ๋ฌธ์ ์ธ์ง ๋น ๋ฅด๊ฒ ํ์ ํ ์ ์๋ค.
→ EmailService๊ฐ ๊ณผ๊ธ ์์คํ ๊ณผ ์ด๋ป๊ฒ ์ฐ๋๋๋์ง๋ฅผ ์ ๋ถ ํตํฉ ํ ์คํธํ์ง ์ ์, EmailService์ ํต์ฌ ๋ก์ง๋ง Mock SMTP ์๋ฒ๋ก ํ ์คํธ ํด๋ณธ๋ค.
๐จ Mocking ์ฌ์ฉ์ ์ฃผ์ํ ์
1. ๊ณผ๋ํ Mock ๋จ๋ฐ
- ๋ชจ๋ ์์กด์ฑ์ Mock์ผ๋ก ๋์ฒดํ๋ฉด, ์ค์ ์ฝ๋์์ ๋์ ์ฐจ์ด๋ฅผ ๋์น ์ ์๋ค.
- ๋ถ๋ฆฌ๋ ํ๊ฒฝ์์๋ง ํ ์คํธํ๋ ๋ฆฌ์คํฌ๊ฐ ์๋ค.
2. Mock ๊ฐ์ฒด์ ์๋ชป๋ ์ค์ (Over-stubbing)
- ๋ถํ์ํ๊ฒ ๋ง์ when() ์กฐ๊ฑด์ ์ค์ ํ๋ฉด ํ ์คํธ๊ฐ ์ง๋์น๊ฒ ์ธ๋ถ ๊ตฌํ์ ์์กด์ ์ด๊ฒ ๋๋ค.
- ์ฝ๋ ๋ฆฌํฉํ ๋ง ์ Mock ์ค์ ๋ถ๋ถ์ด ๊นจ์ง ํ๋ฅ ์ด ๋์์ง๋ค.
3. ํ ์คํธ ์ฝ๋ ์ ์ง๋ณด์
- Mock์ผ๋ก ์ธํ ํ ์คํธ๊ฐ ๋ณต์กํด์ง๋ฉด, ์ค์ ์์คํ ์ด ์๋ "Mock ์๋ฎฌ๋ ์ด์ "๋ง ์ฌ๋ฐ๋ฅด๊ฒ ๋๊ณ ์๋ ์ํฉ์ด ์๊ธธ ์ ์๋ค.
- ํญ์ "์ค์ ๋ก์ง" ๊ณผ "Mock ์ค์ " ๊ฐ์ ๊ฐญ์ด ์๋์ง ์ ๊ฒ์ด ํ์ํ๋ค.
4. ์ ํํ Mock ์ด๋ฆ์ง๊ธฐ
- "MockUserRepository", "StubPatmentService"์ ๊ฐ์ด ๋ช ์์ ์ธ ๋ค์ด๋ฐ์ ์ฐ๋ฉด, ํ ์คํธ์ฝ๋๋ฅผ ์ฝ๋ ์ฌ๋์ด ๋ฐ๋ก ์ธ์งํ ์ ์์ด ์ ์ง๋ณด์๊ฐ ์ฌ์์ง๋ค.
๐ Test Double ์ฉ์ด ์ ๋ฆฌ
- Test Double๋ ์คํดํธ ๋๋ธ(stunt double)์์ ์ฐจ์ฉํ ๊ฒ
- ํ ์คํธ์ค์ธ ์์คํ ์ ์ผ๋ถ๋ถ์ด ์์ ํ ์ค๋น๋์ง ์์๊ฑฐ๋ ํ ์คํธํ๊ธฐ ์ด๋ ค์ด ์ํฉ์์ ๊ทธ ๋์์ผ๋ก ์ฌ์ฉ๋ ์ ์๋ "๊ฐ์ง" ์ปดํฌ๋ํธ๋ฅผ ์๋ฏธํ๋ค.
- ํ ์คํธ ๋๋ธ์ ์ค์ ๊ฐ์ฒด์ ํ๋์ ๋ชจ๋ฐฉํ๋ฉฐ, ํ ์คํธ๊ฐ ํน์ ์กฐ๊ฑด๊ณผ ์ํธ์์ฉ์ ์ฝ๊ฒ ์ฌํํ๊ณ ๊ฒ์ฆํ ์ ์๋๋ก ํ๋ค.
| ์ฉ์ด | ์ ์ | ์ค๋ช | ์์ |
| Dummy | - ์๋ฌด ์ญํ ๋ ์ํํ์ง ์๋๋ค. - "์๋ฆฌ ์ฑ์ฐ๊ธฐ" ์ฉ๋๋ก๋ง ์ฌ์ฉ |
- ๋ฉ์๋ ์๊ทธ๋์ฒ๊ฐ ํน์ ํ์ ์ ์ธ์๋ฅผ ํ์ํ์ง๋ง, ๊ทธ ์ธ์๋ฅผ ์ค์ ๋ก๋ ์ ํ ์ฌ์ฉํ์ง ์์ ๋ | new DummyUser()๊ฐ์ฒด๋ฅผ ๋๊ธฐ๊ธฐ๋ง ํ๊ณ , ์ค์ ๋ก ํด๋น ๊ฐ์ฒด์ ํ๋๋ ๋ฉ์๋๋ฅผ ์ ํ ์ฌ์ฉํ์ง ์์ ๋ |
| Fake | - ์ค์ ๊ตฌํ๊ณผ ์ ์ฌ - ํ๋ก๋์ ํ์ง ์์ค์ด ์๋ ๊ฐ์ด ๊ตฌํ |
- ์ค์ DB ๋์ In-Memory DB ์ฌ์ฉ - ์ค์ ์บ์ ๋์ ๋ฉ๋ชจ๋ฆฌ ๊ธฐ๋ฐ ์๋ฃ๊ตฌ์กฐ ์ฌ์ฉ - Repository ํ ์คํธ๋ ์ค์ DB๋ ์๋์ง๋ง, DB์ฒ๋ผ ๋์ํ๋๋ก ๊ตฌํํ Fake๋ฅผ ์ฌ์ฉํ๋ ๊ฒ๊ณผ ์ ์ฌ |
|
| Stub | - ํธ์ถ์ ๋ํด ๋ฏธ๋ฆฌ ์ ํด์ง ๋ต๋ณ์ ๋ฐํํ๋๋ก ์ค์ ๋ ๊ฐ์ฒด - ํน์ ์ํฉ์ ๊ฐ์ ํด์ ๊ฒฐ๊ณผ๊ฐ์ ๊ณ ์ |
- ์ธ๋ถ ์์กด์ฑ์ ๋ณต์กํ ๋ก์ง์ ํ
์คํธ์์ ๋ถ๋ฆฌ - "๊ณ ์ ๋ ์๋ต"์ ๋ฐํํ๋๋ก ์ธํ ํ ๋ |
- stubRepository.findUser()ํธ์ถ ์ ํญ์ ํน์ User ๊ฐ์ฒด๋ฅผ ๋ฐํํ๋๋ก ์ค์ |
| Spy | - Stub์ ๊ธฐ๋ฅ(๋ฏธ๋ฆฌ ์ ํด์ง ๋ต๋ณ ๋ฐํ) + ์ด๋ค ๋ฉ์๋๊ฐ ๋ช๋ณ ํธ์ถ๋์๋์ง + ์ด๋ค ํ๋ผ๋ฏธํฐ๋ก ํธ์ถ๋์๋์ง ๊ธฐ๋ก๊น์ง ํจ๊ปํด์ฃผ๋ ๊ฐ์ฒด |
- ์ค์ ๋ก์ง ํ๋ฆ์ ๊ทธ๋๋ก ๋ฐ๋ฅด๋ฉด์, "ํธ์ถ ์ด๋ ฅ"์ ๊ด์ฐฐํด์ผ ํ ๋ | - ํน์ ๊ฒฐ๊ณผ๊ฐ์ ๋ฐํํ๋, ์ค๊ฐ์ ๋ก๊น /ํธ์ถ ํ์ ํ์ธ ๋ฑ ๋ถ์์ ํ์ธ์ด ํ์ํ ๊ฒฝ์ฐ. |
| Mock | - ํธ์ถ ๊ณผ์ (ํจ์ ํธ์ถ ํ์, ํ๋ผ๋ฏธํฐ, ์์ ๋ฑ)์ ๋ํ ๊ธฐ๋์น๋ฅผ ๋ฏธ๋ฆฌ ์ค์ ํ๊ณ , ์ค์ ๋ก ๊ทธ๋ ๊ฒ ํธ์ถ๋์๋์ง ๊ฒ์ฆํ ์ ์๋ ๊ฐ์ฒด |
- ์ธ๋ถ ์์กด์ฑ์ ์์ ํ ๋์ฒดํ๊ณ , ํ์ ๊ธฐ๋ฐ ๊ฒ์ฆ์ ํ ๋ ๋ง์ด ์ฌ์ฉํ๋ค. | - Mockito์ when(), verify() ๋ฑ |
๐งญ ํ ์คํธ ์ฝ๋ ์ค์ต
# Given , When , Then
# assertThatThrownBy() : ์ด๋ค ์ฝ๋๊ฐ ์คํ๋ ๋ ์๋ฌ๊ฐ ๋ฐ์ํ๋์ง ํ์ธ --- .isInstanceOf
# assertThat() : ๊ฐ์ด ์ด๋ค ์กฐ๊ฑด๊ณผ ์ผ์นํ๋์ง ํ์ธ(ํ์ธ ํด๋ด!)
# ํ ์คํธ์ฝ๋ ํด๋์ค ์์ฑ : Command + Shift + T
1. Controller ๋จ์ ํ ์คํธ
(1) @WebMvcTest(UserController.class = ํด๋์ค๋ช )
- Web๊ณผ ๊ด๋ จ๋ ์ปดํฌ๋ํธ(@Controller, @ControllerAdvice ๋ฑ)๋ง์ ๋ก๋ํ์ฌ ํ ์คํธ ํ๊ฒฝ์ ๊ตฌ์ฑ
- Service๋ Repository ๋ฑ์ ๋น์ ์๋์ผ๋ก ๋ก๋๋์ง ์์ผ๋ฏ๋ก, ํ ์คํธ์ ํ์ํ ๋ค๋ฅธ ๋ ์ด์ด์ ๋น์ Mocking(@MockBean, @MockitoBean ๋ฑ)ํ์ฌ ์ฃผ์ ํ๋ค.
(2) @MockitoBean
- 3.4.0 ๋ฒ์ ์ด์ ์๋ @MockBean์ด ์ฐ์์ง๋ง ํ์ฌ๋ @MockitoBean์ด ์ฌ์ฉ๋๊ณ ์๋ค.
- given(...).willReturn(...)์ ์ฌ์ฉํ ์ ์๋ค.
(3) @DisplayName("...")
- ํ ์คํธ ๋ฉ์๋์ ๋ํด ์ฌ๋์ด ์ฝ๊ธฐ ์ข์ ์ค๋ช ์ ๋ถ์ฌํ๋ ์ด๋ ธํ ์ด์ ์ด๋ค.
- ํ ์คํธ ๋ฆฌํฌํธ๋ IDE์ ํ ์คํธ ํจ๋์ ํ์๋๋ฏ๋ก ๊ฐ๋ ์ฑ ํฅ์์ ๋์์ ์ค๋๋ค.
(4) @Autowired
- ์์กด์ฑ ์ฃผ์
@WebMvcTest(UserController.class)
class UserControllerTest {
@Autowired
private MockMvc mockMvc;
@MockitoBean
UserService userService;
@Test
@DisplayName("์ด๋ฉ์ผ๋ก ์ฌ์ฉ์ ์กฐํ ์ ์ฑ๊ณต์ ์ผ๋ก JSON ์๋ต์ ๋ฐํ")
void getUserByEmail_shouldReturnUser() throws Exception {
// Given: ์ค๋น - ํน์ ์ด๋ฉ์ผ์ ๊ฐ์ง ์ฌ์ฉ์๊ฐ ์กด์ฌํ๋ค๊ณ ๊ฐ์
String email = "test@example.com";
String name = "ํ๊ธธ๋";
User user = new User(1L,name,email);
given(userService.getUserByEmail(email)).willReturn(user);
// When: ์ก์
- ํด๋น ์ฌ์ฉ์๋ฅผ ์กฐํํ๋ ์ปจํธ๋กค๋ฌ ํธ์ถ
mockMvc.perform(get("/api/users/{email}", email))
.andExpect(status().isOk()) // Then: ๊ฒฐ๊ณผ ํ์ธ - JSON ๊ฒฐ๊ณผ ํ์ธ
.andExpect(jsonPath("$.name").value(equalTo(name)) )
.andExpect(jsonPath("$.email").value(equalTo(email)) );
}
}
.andExpect(jsonPath("$.name").value(equalTo("name")) )
.andExpect(jsonPath("$.email").value(equalTo(email)));
→ "jsonPath" : Response์ ํด๋น ๊ฐ์ด ๋ง๋์ง ํ์ธํ๋ ์์
์ด๋ฏ๋ก Request.jsonPath(๋์ ์ํจ)๊ฐ ์๋ "Result.jsonPath"์ผ๋ก import ํ์!
2. Service ํ ์คํธ
(1) @ExtendWith(MockitoExtension.class)
- MockitoExtension์ @Mock, @InjectMocks ๋ฑ์ ํด์ํ๋ค.
- Mock ๊ฐ์ฒด ์์ฑ ๋ฐ ์ฃผ์ ์ ์๋์ผ๋ก ์ฒ๋ฆฌํ๋ค.
(2) @Mock
- Mockito์์ Mock ๊ฐ์ฒด๋ฅผ ์์ฑํ๊ธฐ ์ํ ์ด๋ ธํ ์ด์ ์ด๋ค.
- when(...), thenReturn(...) ์ผ๋ก ์ง์ (Stubbing)ํ ์ ์๋ค.
(3) @InjeckMocks
- Mockingํ ๋ชจ๋ ์ ๋ค์ userService์ ์ฐ๊ฒฐ(inject)ํ๋ค.
@ExtendWith(MockitoExtension.class)
class UserServiceTest {
@Mock
private UserRepository userRepository;
@InjectMocks // Mockingํ ๋ชจ๋ ์ ๋ค์ userService์ ์ฐ๊ฒฐ(inject)ํ๋ ๊ฒ
private UserService userService;
@Test
@DisplayName("์กด์ฌํ๋ ์ด๋ฉ์ผ๋ก ์ฌ์ฉ์ ์กฐํ ์ ํด๋น ์ฌ์ฉ์ ๋ฐํ")
void getUserByEmail_whenUserExists() {
String email = "existing@example.com";
User mockUser = new User(1L,"user",email);
// Stubbing
when(userRepository.findByEmail(email)).thenReturn(Optional.of(mockUser));
// Service ํธ์ถ
User foundUser = userService.getUserByEmail(email);
// ๊ฒ์ฆ
assertThat(foundUser).isNotNull();
assertThat(foundUser.getEmail()).isEqualTo(email);
}
@Test
@DisplayName("์กด์ฌํ์ง ์๋ ์ด๋ฉ์ผ๋ก ์ฌ์ฉ์ ์กฐํ ์ ์์ธ ๋ฐ์")
void getUserByEmail_whenUserNotExists() {
String email = "notfound@example.com";
when(userRepository.findByEmail(email)).thenReturn(Optional.empty());
assertThatThrownBy(() -> userService.getUserByEmail(email))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("User not found");
}
}
3. Repository ํ ์คํธ
(1) @DataJpaTest
- ๊ธฐ๋ณธ์ ์ผ๋ก ์ธ๋ฉ๋ชจ๋ฆฌ ๋ฐ์ดํฐ๋ฒ ์ด์ค(H2) ๋ฅผ ์ฌ์ฉํ๋๋ก ์ค์ ๋์ด ์๋ค.
- ๋ณ๋์ DB ์ค์ ์์ด๋ ์ํฐํฐ ๋งคํ๊ณผ Repository ๋์์ ๋น ๋ฅด๊ฒ ํ ์คํธํ ์ ์๋ค.
@DataJpaTest
public class UserRepositoryTest {
@Autowired
UserRepository userRepository;
@Test
@DisplayName("H2: ์ด๋ฉ์ผ๋ก ์ฌ์ฉ์ ๊ฒ์ ์ ์กด์ฌํ๋ ์ฌ์ฉ์ ๋ฐํ")
void findByEmail_success() {
// Given
User saved = userRepository.save(new User("user","parksw@naver.com"));
// When
Optional<User> found = userRepository.findByEmail("parksw@naver.com");
// Then
assertThat(found).isPresent();
assertThat(found.get().getId()).isEqualTo(saved.getId());
assertThat(found.get().getEmail()).isEqualTo("parksw@naver.com");
}
@Test
@DisplayName("H2: ์กด์ฌํ์ง ์๋ ์ด๋ฉ์ผ ๊ฒ์ ์ ๋น Optional ๋ฐํ")
void findByEmail_notFound() {
// Given test@example.com์ ์ฃผ์ง ์์
// When
Optional<User> found = userRepository.findByEmail("test@example.com");
// Then
assertThat(found).isEmpty();
}
}
4. <ํผ์ ๊ณต๋ถ> Entity ํ ์คํธ
(1) @AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
- @DataJpaTest๋ ๊ธฐ๋ณธ์ ์ H2๋ฅผ ์ ์ฉํ๋๋ฐ ๋๋ database๋ฅผ mysql๋ก ์ค์ ํด๋์๊ธฐ ๋๋ฌธ์
(2) EntityManager - @PersistenceContext๋ ์ง๊ฟ์ฒ๋ผ ๊ฐ์ด ๋ค๋๋ค!
- EntityManager์ด๋? ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ๋ฐ์ดํฐ๋ฅผ ์ ์ฅํ๊ฑฐ๋ ์กฐํํ๊ฑฐ๋ ์์ ํ๊ฑฐ๋ ์ญ์ ํ ๋ ์ฌ์ฉํ๋ค.(๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ํต)
- @PersistenceContext๋? EntityManager๋ฅผ ์คํ๋ง์ด ์๋์ผ๋ก ์ฃผ์ ํด์ฃผ๋ ์ด๋ ธํ ์ด์ ์ด๋ค.(์ฃผ์ ๋๊ตฌ)
application.properties
...
spring.jpa.database=mysql
@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
class ItemJPQLTest {
@PersistenceContext
private EntityManager entityManager;
// ์๋ต ...
}

