Test Double ์ด๋?
ํ ์คํธ ๋๋ธ์ด๋ ์ค์ ๊ฐ์ฒด๋ฅผ ๋์ ํด์ ํ ์คํ ์์ ์ฌ์ฉํ๋ ๋ชจ๋ ๋ฐฉ๋ฒ์ ์ผ์ปฌ์ด ํธ์นญํ๋ ๊ฒ์ด๋ค.
Test Double
Dummy: ์๋ฌด๊ฒ๋ ํ์ง ์๋ ๊นกํต ๊ฐ์ฒด
Fake: ๋จ์ํ ํํ๋ก ๋์ผํ ๊ธฐ๋ฅ์ ์ํํ๋, ํ๋ก๋์ ์์ ์ฐ๊ธฐ์๋ ๋ถ์กฑํ ๊ฐ์ฒด (ex. FakeRepository)
Stub: ํ ์คํธ์์ ์์ฒญํ ๊ฒ์ ๋ํด ๋ฏธ๋ฆฌ ์ค๋นํ ๊ฒฐ๊ณผ๋ฅผ ์ ๊ณตํ๋ ๊ฐ์ฒด. ๊ทธ ์ธ์๋ ์๋ตํ์ง ์๋๋ค.
Spy: Stub์ด๋ฉด์ ํธ์ถ๋ ๋ด์ฉ์ ๊ธฐ๋กํ์ฌ ๋ณด์ฌ์ค ์ ์๋ ๊ฐ์ฒด. ์ผ๋ถ๋ ์ค์ ๊ฐ์ฒด์ฒ๋ผ ๋์์ํค๊ณ ์ผ๋ถ๋ง Stubbing ํ ์ ์๋ค.
Mock: ํ์์ ๋ํ ๊ธฐ๋๋ฅผ ๋ช ์ธํ๊ณ , ๊ทธ์ ๋ฐ๋ผ ๋์ํ๋๋ก ๋ง๋ค์ด์ง ๊ฐ์ฒด
Mocks Aren't Stubs
Mock๊ณผ Stub์ ๋ค๋ฅด๋ค.
Stub - ์ํ ๊ฒ์ฆ(State Verification)
"mailer๊ฐ mail์ 1๋ฒ ๋ณด๋์ด"๋ผ๋ ์ํ๋ฅผ ๊ฒ์ฆ
// class OrderStateTester...
public void testOrderSendsMailIfUnfilled() {
Order order = new Order(TALISKER, 51);
MailServiceStub mailer = new MailServiceStub();
order.setMailer(mailer);
order.fill(warehouse);
assertEquals(1, mailer.numberSent()); // โ
}
Mock - ํ์ ๊ฒ์ฆ(Behavior Verification)
"mailer์ ๋ฉ์๋๊ฐ ํ ๋ฒ ๋ถ๋ ธ๋ค"๋ผ๋ ํ์ ์์ฒด์ ์ง์ค
// class OrderInteractionTester...
public void testOrderSendsMailIfUnfilled() {
Order order = new Order(TALISKER, 51);
Mock warehouse = mock(Warehouse.class);
Mock mailer = mock(MailService.class);
order.setMailer((MailService) mailer.proxy());
mailer.expects(once()).method("send"); // โ
warehouse.expects(once()).method("hasInventory")
.withAnyArguments()
.will(returnValue(false));
order.fill((Warehouse) warehouse.proxy());
}
https://martinfowler.com/articles/mocksArentStubs.html
Mocks Aren't Stubs
Explaining the difference between Mock Objects and Stubs (together with other forms of Test Double). Also the difference between classical and mockist styles of unit testing.
martinfowler.com
Mock vs Spy
@Mock: ๋ชจ์(mock) ๊ฐ์ฒด๋ฅผ ํ์ฉํ๋ค.
๋ง์ฝ ์ค์ ๊ฐ์ฒด์ ๋ก๊ทธ๋ฅผ ๋จ๊ธฐ๋ ์ฝ๋๊ฐ ์๋ค๋ฉด, @Mock์ ์ฌ์ฉํ์ ๋์๋ ๋น์ฐํ ๋ก๊ทธ๊ฐ ๋จ์ง ์์ ๊ฒ์ด๋ค.
@ExtendWith(MockitoExtension.class) // ํ
์คํธ๊ฐ ์์๋ ๋ Mockito ์ฌ์ฉํด์ ๋ชจ์(mock) ๊ฐ์ฒด๋ฅผ ๋ง๋ ๋ค.
class MailServiceTest {
@Mock
private MailSendClient mailSendClient;
@DisplayName("๋ฉ์ผ ์ ์ก ํ
์คํธ")
@Test
void sendMail() {
// given
when(mailSendClient.sendEmail(anyString(), anyString(), anyString(), anyString()))
.thenReturn(true);
// ...
}
}
@Spy: ์ค์ ๊ฐ์ฒด๋ฅผ ํ์ฉํ๋ค.
๋ง์ฝ ์ค์ ๊ฐ์ฒด์ ๋ก๊ทธ๋ฅผ ๋จ๊ธฐ๋ ์ฝ๋๊ฐ ์๋ค๋ฉด, @Spy๋ฅผ ์ฌ์ฉํ๋ฉด ๋ก๊ทธ๊ฐ ๋จ๋๋ค.
@ExtendWith(MockitoExtension.class) // ํ
์คํธ๊ฐ ์์๋ ๋ Mockito ์ฌ์ฉํด์ ๋ชจ์(mock) ๊ฐ์ฒด๋ฅผ ๋ง๋ ๋ค.
class MailServiceTest {
@Spy
private MailSendClient mailSendClient;
@DisplayName("๋ฉ์ผ ์ ์ก ํ
์คํธ")
@Test
void sendMail() {
// given
doReturn(true)
.when(mailSendClient)
.sendEmail(anyString(), anyString(), anyString(), anyString());
// ...
}
}
์ผ๋ถ๋ ์ค์ ๊ฐ์ฒด๋ฅผ ์ฌ์ฉํ๊ณ ์ถ๊ณ , ์ผ๋ถ๋ ๋ชจ์ ๊ฐ์ฒด๋ฅผ ์ฌ์ฉํ๊ณ ์ถ์ ๋,
@Spy์ @Mock์ ์์ด์ ์ฌ์ฉํ๋ฉด ๋๋ค.
@ExtendWith(MockitoExtension.class) // ํ
์คํธ๊ฐ ์์๋ ๋ Mockito ์ฌ์ฉํด์ ๋ชจ์(mock) ๊ฐ์ฒด๋ฅผ ๋ง๋ ๋ค.
class MailServiceTest {
@Spy
private MailSendClient mailSendClient;
@Mock
private MailSendHistoryRepository mailSendHistoryRepository;
@InjectMocks
private MailService mailService;
// ...
}
'๐ป Knowledge > ํ ์คํธ์ฝ๋' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
deleteAllInBatch() vs deleteAll() ๋ฉ์๋์ ์ฐจ์ด (0) | 2024.12.31 |
---|---|
๋ ๋์ ํ ์คํธ๋ฅผ ์์ฑํ๊ธฐ ์ํ ๊ตฌ์ฒด์ ์กฐ์ธ + ๋์ ์๊ฐ (0) | 2024.12.31 |
Presentation Layer ํ ์คํธ (1) | 2024.12.27 |
@SpringBootTest vs @DataJpaTest (0) | 2024.12.19 |
Persistence Layer ํ ์คํธ (2) | 2024.12.19 |