โ๏ธ์๋ฌ ๋ฉ์์ง
org.mockito.exceptions.verification.opentest4j.ArgumentsAreDifferent
๐ป ๊ฐ๋ฐ ํ๊ฒฝ
- Framework : `Spring Boot 3.2.3`
- DB : `MySQL`(๋ก์ปฌ), `H2`(ํ ์คํธ)
- Library : `JPA`, `QueryDSL 5.0.0`
- Test : `JUnit`
๐ ์์ธ
Spring์์ Junit์ผ๋ก Controller ๋จ์ ๊ฒ์๊ธ ์์ ๊ธฐ๋ฅ์ ํ ์คํธํ๋๋ฐ, `verify()` ๋ถ๋ถ์์ ๊ฒ์ฆ์๋ฌ๊ฐ ๋ฌ๋ค.
@Nested
@DisplayName("๊ตฌ์ธ ๊ฒ์๊ธ ์์ ")
public class updateHiringPostTest {
private Long postId;
private HiringPostReq hiringPostReq;
private String uid;
@BeforeEach
void setUp() {
postId = 1L;
hiringPostReq = HiringPostReq.builder()
...
.build();
uid = "test-uid";
}
@Test
@DisplayName("๊ตฌ์ธ ๊ฒ์๊ธ ์์ ์ฑ๊ณต")
public void updateHiringPost_success() throws Exception {
// given
doNothing().when(hiringPostService).updateHiringPost(eq(postId), eq(hiringPostReq), eq(uid));
// when, then
mockMvc...
// ์ฌ๊ธฐ์ ์๋ฌ๋จ
verify(hiringPostService, times(1)).updateHiringPost(eq(postId), eq(hiringPostReq), eq(uid));
}
}
`hiringPostService.updateHiringPost` ๋ฉ์๋์ ํธ์ถ์ mock์ผ๋ก ์ค์ ํ๊ณ , `verify()`๋ก ์ธ์๋ค์ ๊ฐ์ด ๊ฐ์์ง๋ฅผ ๊ฒ์ฆํ๋๋ก ์ฝ๋๋ฅผ ์์ฑํ์๋ค.
ํ์ง๋ง ํ ์คํธ๋ฅผ ์คํํด๋ณด๋ postId์ uid์ ๊ฐ์ ๊ฐ์๋ฐ, hiringpostReq ๊ฐ์ฒด์ ๊ฐ๋ง ๋ฌ๋๋ค.
Expected(์์ ๊ฐ)
xxx.HiringPostService#0 bean.updateHiringPost(
1L,
xxx.HiringPostReq@3eadad14,
"test-uid"
);
Actual(์ค์ ๊ฐ)
xxx.HiringPostService#0 bean.updateHiringPost(
1L,
xxx.HiringPostReq@4acc27fd,
"test-uid"
);
`3eadad14`์ `4acc27fd`๋ก ๊ฐ์ฒด์ ๋ฉ๋ชจ๋ฆฌ ์ฃผ์ ๊ฐ์ด ๋ค๋ฅด๋ค.
๋น์ฐํ mock ๊ฐ์ฒด์ new๋ฅผ ํตํด ์์ฑ๋ ๊ฐ์ฒด๋ ๋ฉ๋ชจ๋ฆฌ ์์์ ๋ค๋ฅธ ์ฃผ์ ๊ฐ์ ๊ฐ๋๋ค.
์์ธ์ ํ ์คํธ ์ฝ๋์์ ์ฐ๋ Mockito์ `eq()` ๋ฉ์๋์ ์๋ค.
`eq()` ๋ฉ์๋๋ ๊ฐ์ฒด์ ๋น๊ต๋ฅผ ์ํด `equals()` ๋ฉ์๋๋ฅผ ์ฌ์ฉํ๋ค. ์๋ฐ์์ `equals()` ๋ฉ์๋๋ ๊ฐ์ฒด์ ๋ฉ๋ชจ๋ฆฌ ์ฃผ์ ๊ฐ์ ๋น๊ตํ๋ค.
๊ทธ๋์ ๋ ๊ฐ์ฒด์ ์ฃผ์ ๊ฐ์ด ๋ฌ๋ผ false๋ฅผ ๋ฐํํด ํ ์คํธ์ ์คํจํ๋ ๊ฒ์ด๋ค.
๋ ๊ฐ์ฒด์ ์ฃผ์ ๊ฐ์ด ๋ฌ๋ผ๋ ๋ด์ฉ์ด ๊ฐ๋ค๊ณ ๊ฐ์ ํ๊ณ true๋ฅผ ๋ฐํํ๋๋ก ํ๋ ค๋ฉด ์ด๋ป๊ฒ ํด์ผ ํ ๊น?
๐ก ํด๊ฒฐ
๋ ๊ฐ์ฒด์ ๋น๊ต ๊ธฐ์ค์ ์ํํด์ผ ํ๋ค.
๊ทธ๋ผ ๊ฐ์ฒด์ ๋น๊ต ๊ธฐ์ค์ ์ด๋ป๊ฒ ์ํํ ์ ์์๊น?
๋ฐฉ๋ฒ 1 : any() ์ฌ์ฉ
verify(hiringPostService, times(1)).updateHiringPost(eq(postId), any(HiringPostReq.class), eq(uid));
any()๋ ์ค์ ๋ก ๊ฐ์ฒด์ ๊ฐ์ ๋น๊ตํ๋ ๊ฒ์ด ์๋๋ผ, ๊ฐ์ฒด๊ฐ ์ ๋ฌ๋ ๊ฒ์ด๋ผ๊ณ ๊ฐ์ ํ๋ ๊ฒ์ด๋ค. ๊ฐ์ฒด์ ๊ฐ ๊ฒ์ฆ์ ์ด๋ ต์ง๋ง ํด๋น ๋ฉ์๋๊ฐ ํธ์ถ์ด ๋์๋์ง๋ง ํ์ธํ๋ ์ฉ๋๋ก ์ธ ์ ์๋ค.
๋ฐฉ๋ฒ 2 : @EqualsAndHashCode ์ถ๊ฐ
Lombok์ ์ฌ์ฉํ๊ณ ์๋ค๋ฉด, `@EqualsAndHashCode`๋ฅผ ์ถ๊ฐํ๋ฉด ๋๋ค. Lombok์์๋ ๊ธฐ๋ณธ์ผ๋ก ํ๋ ๊ฐ์ ๊ธฐ์ค์ผ๋ก ๋น๊ต๋ฅผ ํ๊ฒ ๋๋ค.
@Getter
@EqualsAndHashCode //์ถ๊ฐ
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class HiringPostReq {
...
}
`@EqualsAndHashCode`๋ `@Data`์ ํฌํจ๋์ด ์๊ธฐ๋ ํ๋ค. `@Data`๋ฅผ ์ฐ๊ณ ์์๋ค๋ฉด ์๋ฌ๊ฐ ๋์ง ์์์ํ ๋ฐ, ๋๋ ๋ฌด๋ถ๋ณํ ์ด๋ ธํ ์ด์ ์ฌ์ฉ์ผ๋ก ์ธํ ๋ถ์์ฉ์ ๋ง๊ธฐ ์ํด `@Data` ์ฌ์ฉ์ ์ง์ํ๊ณ ์๊ธฐ ๋๋ฌธ์ ์๋ฌ๊ฐ ๋ ๊ฒ์ด๋ค.
ํ์ง๋ง, `@EqualsAndHashCode` ์ฌ์ฉ๋ ์ฃผ์ํด์ผ ํ๋ค. DTO๋ ๋ฌธ์ ๊ฐ ์๊ฒ ์ง๋ง, Entity์ ์ธ ๋๋ ๋ฌธ์ ๊ฐ ๋ฐ์ํ ์๋ ์๋ค.
๋ชจ๋ ํ๋๋ฅผ ๊ธฐ๋ฐ์ผ๋ก equals()์ hashCode()๋ฅผ ์๋์ผ๋ก ์์ฑํ๊ธฐ ๋๋ฌธ์ด๋ค. JPA๋ฅผ ์ฌ์ฉํ๋ค๋ฉด, ์ฐ๊ด ๊ด๊ณ๊ฐ ์๋ ํ๋์ ๊ฒฝ์ฐ ์ง์ฐ ๋ก๋ฉ์ด ๊ฐ์ ๋ก ๋ฐ์ํ ์ ์๋ค. ๊ทธ๋ฆฌ๊ณ ์๋ฐฉํฅ ๊ด๊ณ์ผ ๋, ์ํ ์ฐธ์กฐ๊ฐ ๋ฐ์ํ ์๋ ์๋ค๋ ์ ๋ ๋ฌธ์ ๋ค.
๋ด๊ฐ ๊ฒ์ฆํ๋ ค๋ ๋ถ๋ถ์ DTO์ด๊ธฐ ๋๋ฌธ์ `@EqualsAndHashCode`๋ฅผ ์ฌ์ฉํด๋ ๋ฌธ์ ๊ฐ ์์ ๊ฒ์ด๋ผ๊ณ ํ๋จํ์ฌ ๋ฐฉ๋ฒ 2๋ฅผ ์ฌ์ฉํ๋ค.
ํ์ง๋ง Entity๋ฅผ ๊ฒ์ฆํด์ผ ํ๋ค๋ฉด `any()`๋ฅผ ์ฌ์ฉํ์ ๊ฒ์ด๋ค.