[์ฐธ๊ณ ] Mockito ๊ธฐ์ด ์ฌ์ฉ๋ฒ
๐บ Mockito๋
๋ชจ์ ๊ฐ์ฒด ์์ฑ, ๊ฒ์ฆ, ์คํ์ ์ง์ํ๋ ํ๋ ์์ํฌ์ด๋ค. ์ด๋, ์ธ๊ธฐ์๋ ์๋ฐ ๋ชจ์ ๊ฐ์ฒด ํ๋ ์์ํฌ ์ค ํ๋๋ก, ์ด๋ฅผ ํตํด ๋์ญ์ ๋ณด๋ค ์ํํ๊ฒ ์ฌ์ฉํ ์ ์๊ฒ ๋๋ค.
๐บ dependencies ์ถ๊ฐ (Gradle)
dependencies {
implementation 'org.springframework.boot:spring-boot-starter'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
// junit
testImplementation('org.junit.jupiter:junit-jupiter:5.5.0')
// mockito -> ์ด๋ถ๋ถ ์ถ๊ฐ
testImplementation('org.mockito:mockito-core:4.5.1')
}
๐บ ๋ชจ์ ๊ฐ์ฒด ์์ฑ
@Test
void mockTest(){
GameNumGen getMock = mock(GameNumGen.class); // ๋ชจ์ ๊ฐ์ฒด ์์ฑ ์
}
}
- Mocktio.mock(): ํด๋์ค, ์ธํฐํ์ด์ค, ์ถ์ ํด๋์ค์ ๋ํด ๋ชจ์ ๊ฐ์ฒด๋ฅผ ์์ฑํ ์ ์์'
๐บ ์คํ ์ค์
๋ชจ์ ๊ฐ์ฒด๋ฅผ ์ค์ ํ ์ดํ์๋ BDDMockito ํด๋์ค๋ฅผ ์ด์ฉํ์ฌ ๋ชจ์ ๊ฐ์ฒด์ ์คํ ์ ๊ตฌ์ฑํญํ ์ ์๋ค.
@Test
void mockTest(){
GameNumGen genMock = mock(GameNumGen.class); // ๋ชจ์ ๊ฐ์ฒด ์์ฑ ์
given(genMock.generate(GameLevel.EASY)).willReturn("123"); // given: ๋ชจ์ ๊ฐ์ฒด ๋งค์๋ ํธ์ถ์ ์ ๋ฌ
String num = genMock.generate(GameLevel.EASY);
assertEquals("123", num);
}
@Test
void mockThrowTest(){
GameNumGen genMock = mock(GameNumGen.class);
given(genMock.generate(null)).willThrow(IllegalArgumentException.class);
// given(genMock.generate(null)).willThrow(new IllegalArgumentException()); ์ด๊ฒ๋ ๊ฐ๋ฅ
assertThrows(
IllegalArgumentException.class,
() -> genMock.generate(null)
);
}
- given(): ์คํ ์ ์ ์ํ ๋ชจ์ ๊ฐ์ฒด์ ๋ฉ์๋ ํธ์ถ์ ์ ๋ฌ
- willReturn(): ์คํ ์ ์ ์ํ ๋ฉ์๋๊ฐ ๋ฆฌํดํ ๊ฐ์ ์ง์
- willThrow(): ์คํ ์ ์ ์ํ ๋ฉ์๋๊ฐ ๋ฆฌํดํ ์์ธ์ฒ๋ฆฌ ์ง์
์ฆ, ์์ ์ฝ๋์์ generate๋ฅผ ์คํํ๋ฉด given์์ ์ง์ ํ willReturn ๊ฐ์ผ๋ก 123์ ๋๊ฒจ์ค๋ค.
return ํ์ ์ด void์ธ method์ ๊ฒฝ์ฐ์๋ ๋ค์๊ณผ ๊ฐ์ด Test ์ฝ๋๋ฅผ ์์ฑํ๋ค. ๋ฉ์๋ ๋ค์ willThrow๊ฐ ๋ถ๋ ๊ฒ์ด ์๋, ์ต์ ์ ํ์ , ๊ฐ์ฒด๋ฅผ ์ธ์๋ก ๋ฐ๊ณ ๋ ํ, given ๋ฉ์๋๋ฅผ ํตํด์ ๋ชจ์ ๊ฐ์ฒด๋ฅผ ์ ๋ฌ๋ฐ๋๋ค.
๋จ, willThrow ๋ค์ ๋ถ๋ given ์ ๊ฒฝ์ฐ์๋ ๋ชจ์ ๊ฐ์ฒด์ ๋ฉ์๋๊ฐ ์๋ ๋ชจ์ ๊ฐ์ฒด ๊ทธ ์์ฒด์ธ ๊ฒ์ ์ ์ํด์ผ ํ๋ค.
@Test
void voidMethodWillThrowTest(){
List<String> mockList = mock(List.class);
willThrow(UnsupportedOperationException.class)
.given(mockList)
.clear();
assertThrows(
UnsupportedOperationException.class,
() -> mockList.clear()
);
}
๐ผ ์ธ์ ๋งค์นญ ์ฒ๋ฆฌ
given(genMock.generate(GameLevel.EASY)).willReturn("123");
String num = genMock.generate(GameLevel.NORMAL);
๋ค์๊ณผ ๊ฐ์ด ์ฝ๋๊ฐ ์์ฑ์ด ๋์ด ์์ ๊ฒฝ์ฐ์๋ EASY enum์์๋ง 123 return ๊ฐ์ด ์์ผ๋ฏ๋ก NORMAL์ ๋ฉ์๋ ํธ์ถ ํ ๋ ๊ฐ์ด ์๊ธฐ ๋๋ฌธ์ null ์ด return ๋๋ค.
์ด์ ๊ฐ์ด, Mockito๋ ์คํ ์ค์ ๊ณผ ์ผ์นํ๋ ์ธ์๊ฐ ์๋ ๊ฒฝ์ฐ์๋ ๋ฆฌํด ํ์ ์ ๊ธฐ๋ณธ ๊ฐ์ return ํ๋ค. int์ ๊ฒฝ์ฐ์๋ 0, boolean์ ๊ฒฝ์ฐ์๋ false์ ๊ฐ์ด ๋ง์ด๋ค. ๊ธฐ๋ณธ ๋ฐ์ดํฐ ํ์ ์ด ์์ ๊ฒฝ์ฐ์๋ null์ return ํ๋ค.
@Test
void anyMatchTest(){
GameNumGen genMock = mock(GameNumGen.class);
given(genMock.generate(any())).willReturn("456");
String num = genMock.generate(GameLevel.EASY);
assertEquals("456", num);
String num2 = genMock.generate(GameLevel.NORMAL);
assertEquals("456", num2);
}
- any(): ๋ชจ๋ ๊ฐ์ ์ผ์นํ๋๋ก ์คํ ์ค์ , ์์์ ๊ฐ์ ๋ํ ์ผ์น
- anyInt(), anyShort(), anyLong(), anyByte, anyChar(), anyDouble() ๋ฑ๋ฑ
- anyString()
- anyList(), anySet(), anyMap(), anyCollection(): ์์์ ์ฝ๋์ ์ ๋ํ ์ผ์น
- matches(String), matches(Pattern): ์ ๊ท ํํ ์์ ์ด์ฉํ String ๊ฐ ์ผ์น ์ฌ๋ถ
- eq(๊ฐ): ํน์ ๊ฐ๊ณผ ์ผ์น ์ฌ๋ถ
๋ํ, ์์์ ๊ฐ๊ณผ ์ผ์นํ๋ ์ธ์์ ์ ํํ๊ฒ ์ผ์นํ๋ ์ธ์๋ฅผ ํจ๊ป ์ฌ์ฉํ๊ณ ์ถ๋ค๋ฉด eq() ๋ฉ์๋๋ฅผ ์ฌ์ฉํด์ผํ๋ค.
given(mockList.set(anyInt(), "123")).willReturn("456"); // ์๋ฌ
given(mockList.set(anyInt(), eq("123"))).willReturn("456"); // ํด๊ฒฐ
๐บ ํ์ ๊ฒ์ฆ
๋ชจ์ ๊ฐ์ฒด์ ์ญํ ์ค ํ๋๋ ์ค์ ๋ชจ์ ๊ฐ์ฒด๊ฐ ๋ถ๋ ธ๋์ง ๊ฒ์ฆํ๋ ๊ฒ์ด๋ค. ๋ค์ ์ฝ๋๋ฅผ ํ์ธํด๋ณด๋ฉด genMock์ ๋ชจ์ ๊ฐ์ฒด์ generate ๋ฉ์๋๊ฐ EASY ์ธ์๋ฅผ ์ฌ์ฉํด์ ์ค์ ํธ์ถ๋์๋์ง ๊ฒ์ฆ์ ํ๋ค.
@Test
void init(){
GameNumGen genMock = mock(GameNumGen.class);
Game game = new Game(genMock);
game.init(GameLevel.EASY);
then(genMock).should().generate(GameLevel.EASY); // then : ๋ชจ์ ๊ฐ์ฒด๋ฅผ ์ ๋ฌ ๋ฐ์ should : ๋ชจ์ ๊ฐ์ฒด์ ๋ฉ์๋๊ฐ ๋ถ๋ ค์ผ ํ๋ค๋ ๊ฒ์ ์ค์
}
์ ํํ ๊ฐ์ด ์๋๋ผ, ํธ์ถ ๋์๋์ง๋ง ํ์ธํ๊ณ ์ถ์ผ๋ฉด any() ํจ์๋ฅผ ์ฌ์ฉํ๋ฉด ๋๋ค.
๋ฉ์๋ ํธ์ถ ํ์๋ฅผ ๊ฒ์ฆํ๊ธฐ ์ํด Mockito ํด๋์ค๊ฐ ์ ๊ณตํ๋ ๋ฉ์๋๋ ๋ค์๊ณผ ๊ฐ๋ค.
- only() : ํ๋ฒ๋ง ํธ์ถ
- times(int) : ์ง์ ํ ํ์๋งํผ ํธ์ถ
- never() : ํธ์ถํ์ง ์์
- atLeast(int) ; ์ ์ด๋ ์ง์ ํ ํ์๋งํผ ํธ์ถ
- atLeastOnce() : atLeast(1)
- atMost(int) : ์ต๋ ์ง์ ํ ํ์๋งํผ ํธ์ถ
๐บ ์ธ์ ์บก์ณ
๋จ์ ํ ์คํธ๋ฅผ ์งํํ๋ค๋ณด๋ฉด, ๋ชจ์ ๊ฐ์ฒด๋ฅผ ํธ์ถํ ๋ ์ฌ์ฉํ ์ธ์๋ฅผ ๊ฒ์ฆํด์ผํ ๋๊ฐ ์๋ค. String, int์ ๊ฒฝ์ฐ๋ ํ์ธํ๊ธฐ ์ฝ์ง๋ง ๋ง์ ์์ฑ์ ๊ฐ๊ณ ์๋ ๊ฐ์ฒด์ ๊ฒฝ์ฐ ์ฝ๊ฒ ๊ฒ์ฆํ๊ธฐ๋ ์ฝ์ง ์๋ค. ์ด ๋ ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ์ด ์ธ์ ์บก์ณ์ด๋ค.
์ธ์ ์บก์ณ์ ๊ด๋ จ๋ ์ฝ๋๋ ๋ค์๊ณผ ๊ฐ๋ค.
@DisplayName("๊ฐ์
ํ๋ฉด ๋ฉ์ผ์ ์ ์กํจ")
@Test
void whenRegisterThenSendMail(){
userRegister.register("id", "pw", "email");
ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class); // String ํ์
์ ์ธ์ ๋ณด๊ด
then(mockEmailNotifier)
.should().sendRegisterEmail(captor.capture());
String realEmail = captor.getValue();
assertEquals("email", realEmail);
}