Mocking Framework - Mockito
Mockito๋ Java ๋จ์ ํ ์คํธ(Unit Test) ์์ฑ์ ๋๋ ๋ชจํน(Mocking) ํ๋ ์์ํฌ๋ก, ์ค์ ์์กด ๊ฐ์ฒด ๋์ ๊ฐ์ง ๊ฐ์ฒด(Mock Object)๋ฅผ ์์ฑํ๊ณ ์ ์ดํ๋ ๊ธฐ๋ฅ์ ์ ๊ณตํ๋ค.
ํ ์คํธ ํ๊ฒฝ ๊ฒฉ๋ฆฌ: ์ธ๋ถ ์์ธ(DB, ๋คํธ์ํฌ, ๋ค๋ฅธ ํด๋์ค์ ๋ด๋ถ ๋ก์ง ๋ฑ)์ ์ํฅ์ ๋ฐ์ง ์๊ณ ํ ์คํธ ๋์์ ๋ก์ง์๋ง ์ง์คํ ์ ์๋๋ก ์ง์
ํ๋ ์ ์ด ๋ฐ ๊ฒ์ฆ: ์ํ๋ ๋ชจ๋ ์ํฉ์ ์๋ฎฌ๋ ์ด์ (Stubbing)ํ๊ณ , ๋ฉ์๋๊ฐ ์์๋๋ก ํธ์ถ๋์๋์ง(Verification) ๊ฒ์ฆํ๋ ๊ฐ๋ ฅํ ๊ธฐ๋ฅ์ ์ ๊ณต
๊ฐ๋ ์ฑ ๋์ ํ ์คํธ: BDD(Behavior-Driven Development) ์คํ์ผ์ ์ง์ํ๋ ๋ฑ, ํ ์คํธ ์ฝ๋์ ์๋๋ฅผ ๋ช ํํ๊ฒ ํํํ ์ ์๋๋ก ์ง์
1. Mock ๊ฐ์ฒด ์์ฑ
์์กด์ฑ์ ๋์ฒดํ ๊ฐ์ง ๊ฐ์ฒด๋ฅผ ๋ง๋๋ ๋ฐฉ๋ฒ์ ํฌ๊ฒ ๋ ๊ฐ์ง๋ค.
Mockito.mock()
๋ฉ์๋ ์ฌ์ฉ
Mockito.mock()
๋ฉ์๋ ์ฌ์ฉimport static org.mockito.Mockito.mock;
class MemberServiceTest {
MemberRepository mockMemberRepository = mock(MemberRepository.class);
}
@Mock
์ด๋
ธํ
์ด์
์ฌ์ฉ
@Mock
์ด๋
ธํ
์ด์
์ฌ์ฉํ
์คํธ ํด๋์ค ์๋จ์ @ExtendWith(MockitoExtension.class)
๋ฅผ ์ ์ธํ์ฌ Mockito ํ์ฅ ๊ธฐ๋ฅ์ ํ์ฑํํ ํ, ํ๋์ @Mock
์ด๋
ธํ
์ด์
์ ๋ถ์ฌ ์ฌ์ฉํ๋ค.
@Mock
: ํด๋น ํ๋๋ฅผ Mock ๊ฐ์ฒด๋ก ์ด๊ธฐํ@InjectMocks
: ํ ์คํธ ๋์ ๊ฐ์ฒด๋ฅผ ์์ฑํ๊ณ ,@Mock
๋๋@Spy
๋ก ์์ฑ๋ ์์กด ๊ฐ์ฒด๋ฅผ ์๋์ผ๋ก ์ฃผ์
@ExtendWith(MockitoExtension.class)
class MemberServiceTest {
@Mock
private MemberRepository memberRepository; // MemberRepository์ Mock ๊ฐ์ฒด๊ฐ ์ฃผ์
@InjectMocks
private MemberService memberService; // ํ
์คํธ ๋์ ๊ฐ์ฒด, @Mock ๊ฐ์ฒด์ธ memberRepository๊ฐ ์ฃผ์
}
@Spy
- ์ค์ ๊ฐ์ฒด ์ผ๋ถ Mocking
@Spy
- ์ค์ ๊ฐ์ฒด ์ผ๋ถ Mocking์ค์ ๊ฐ์ฒด๋ฅผ ์ฌ์ฉํ๋ฉด์ ํน์ ๋ฉ์๋์ ํ๋๋ง ๋ณ๊ฒฝํ๊ณ ์ถ์ ๋ ์ฌ์ฉํ๋ค.(Stubbing ํ์ง ์์ ๋ฉ์๋๋ ์ค์ ๋ก์ง์ ์ํ)
@Spy
private List<String> spiedList = new ArrayList<>();
@Test
void spyTest() {
// ์ค์ add ๋ฉ์๋๊ฐ ํธ์ถ๋จ
spiedList.add("one");
// size() ๋ฉ์๋๋ง Stubbing
doReturn(100).when(spiedList).size();
assertEquals(100, spiedList.size()); // Stubbing๋ ๊ฐ ๋ฐํ
assertEquals("one", spiedList.get(0)); // ์ค์ get ๋ฉ์๋ ๋์
}
2. Stubbing - ํ๋ ์ ์
Mock ๊ฐ์ฒด๊ฐ ํน์ ๋ฉ์๋ ํธ์ถ์ ์ด๋ป๊ฒ ์๋ตํ ์ง ๋ฏธ๋ฆฌ ์ ์ํ๋ ๊ณผ์ ์ด๋ค.
when()
๊ณ์ด
when()
๊ณ์ด๋ฐํ๊ฐ์ด ์๋ ๋ฉ์๋์ ํ๋์ ์ ์ํ ๋ ์ฌ์ฉํ๋ค.
thenReturn(value)
: ๊ณ ์ ๋ ๊ฐ์ ๋ฐํthenThrow(exception)
: ์์ธ ๋ฐ์thenAnswer(answer)
: ๋์ ์ธ ๋ก์ง์ ํตํด ๊ณ์ฐ๋ ๊ฐ์ ๋ฐํ
@Test
void exmaple() {
// findById(1L) ํธ์ถ ์ member ๊ฐ์ฒด ๋ฐํ
when(memberRepository.findById(1L))
.thenReturn(Optional.of(member));
// findById(2L) ํธ์ถ ์ ์์ธ ๋ฐ์
when(memberRepository.findById(2L))
.thenThrow(new RuntimeException("Member not found"));
// ์ฐ์ ํธ์ถ์ ๋ํด ๋ค๋ฅธ ํ๋ ์ ์
when(mock.someMethod())
.thenReturn("first call")
.thenReturn("second call");
// ๋์ ์ธ ์๋ต ์์ฑ
when(mock.calculate(anyInt()))
.thenAnswer(invocation -> {
int arg = invocation.getArgument(0);
return arg * 2;
});
}
do*()
๊ณ์ด
do*()
๊ณ์ด๋ฐํ๊ฐ์ด void
์ธ ๋ฉ์๋๋, ์ค์ ๋ฉ์๋ ํธ์ถ์ ํผํ๋ฉด์ Stubbing ํด์ผ ํ๋ Spy ๊ฐ์ฒด์ ์ฌ์ฉ๋๋ค.
doNothing()
: ์๋ฌด ๋์๋ ํ์ง ์์doThrow(exception)
: ์์ธ ๋ฐ์doAnswer(answer)
: ๋์ ์ธ ๋ก์ง์ ์คํdoReturn(value)
:when()
๋์ ์ฌ์ฉํ๋ Stubbing(Spy ๊ฐ์ฒด์ ๊ถ์ฅ)
@Test
void exmaple() {
// delete(member)๊ฐ ํธ์ถ๋๋ฉด ์๋ฌด ๋์๋ ํ์ง ์๋๋ก ์ ์
doNothing()
.when(memberRepository)
.delete(member);
// delete(member)๊ฐ ํธ์ถ๋๋ฉด ์์ธ ๋ฐ์
doThrow(new IllegalArgumentException())
.when(memberRepository)
.delete(member);
}
3. Verification - ํ์ ๊ฒ์ฆ
ํ ์คํธ ๋์ ๋ก์ง์ด ์คํ๋ ํ, Mock ๊ฐ์ฒด์ ๋ฉ์๋๊ฐ ์์๋๋ก ํธ์ถ๋์๋์ง ๊ฒ์ฆํ๋ค.
times(n)
: ์ ํํn
๋ฒ ํธ์ถ๋์๋์ง ๊ฒ์ฆverify(mock)
: ํด๋น ๋ฉ์๋๊ฐ 1๋ฒ ํธ์ถ๋์๋์ง ๊ฒ์ฆ(=times(1)
)never()
: ํ ๋ฒ๋ ํธ์ถ๋์ง ์์๋์ง ๊ฒ์ฆ(=times(0)
)atLeast(n)
/atMost(n)
: ์ต์n
๋ฒ / ์ต๋n
๋ฒ ํธ์ถ๋์๋์ง ๊ฒ์ฆinOrder(mock...)
: ์ฌ๋ฌ Mock ๊ฐ์ฒด์ ๊ฑธ์ณ ํธ์ถ ์์๊ฐ ์ ํํ์ง ๊ฒ์ฆtimeout(millis)
: ๋น๋๊ธฐ ์ฝ๋ ํ ์คํธ ์, ์ง์ ๋ ์๊ฐ ๋ด์ ๋ฉ์๋๊ฐ ํธ์ถ๋๋์ง ๊ฒ์ฆ
@Test
void exmaple() {
// deleteById(1L)๊ฐ ์ ํํ 1๋ฒ ํธ์ถ๋์๋์ง ๊ฒ์ฆ
verify(memberRepository, times(1))
.deleteById(1L);
// deleteById(2L)๋ ํธ์ถ๋์ง ์์๋์ง ๊ฒ์ฆ
verify(memberRepository, never())
.deleteById(2L);
// ๋น๋๊ธฐ ์์
ํ 100ms ์์ notify ๋ฉ์๋๊ฐ ํธ์ถ๋์๋์ง ๊ฒ์ฆ
verify(notificationService, timeout(100))
.notify(any(User.class));
// ์์ ๊ฒ์ฆ
InOrder inOrder = inOrder(memberRepository, emailService);
inOrder.verify(memberRepository)
.save(any(Member.class)); // save๊ฐ ๋จผ์
inOrder.verify(emailService)
.sendWelcomeEmail(any(Member.class)); // email ๋ฐ์ก์ด ๋์ค์
}
4. Argument Matchers & Captors
Argument Matchers
Stubbing์ด๋ ๊ฒ์ฆ ์, ์ธ์์ ์ค์ ๊ฐ ๋์ ์ ์ฐํ ์กฐ๊ฑด์ ์ฌ์ฉํ๊ณ ์ถ์ ๋ ํ์ฉํ๋ค.
any()
: ๋ชจ๋ ํ์ ์ ๊ฐ์ฒด๋ฅผ ํ์ฉ(anyString()
,anyInt()
๋ฑ ํ์ ๋ณ Matcher ์ ๊ณต)eq(value)
: ํน์ ๊ฐ๊ณผ ๋์ผํด์ผ ํจ์ ๋ช ์argThat(matcher)
: ์ปค์คํ Matcher๋ฅผ ์ฌ์ฉํ์ฌ ๋ณต์กํ ์กฐ๊ฑด ์ง์ ๊ฐ๋ฅ
์ค์ํ ์ ์ ๋ฉ์๋์ ์ฌ๋ฌ ์ธ์ ์ค ํ๋๋ผ๋ Argument Matcher๋ฅผ ์ฌ์ฉํ๋ค๋ฉด, ๋ชจ๋ ์ธ์๋ฅผ Matcher๋ก ์์ฑํด์ผ ํ๋ค.
@Test
void exmaple() {
// eq()๋ฅผ ์ฌ์ฉํ์ฌ ์ผ๋ฐ ๊ฐ์ Matcher๋ก ๋ณํ
when(memberRepository.save(eq("user"), anyInt()))
.thenReturn(member);
}
Last updated
Was this helpful?