Mock

Mock์€ "๋ชจ์˜, ๊ฐ€์งœ์˜"๋ผ๋Š” ๋œป์œผ๋กœ, ํ…Œ์ŠคํŠธ์‹œ ์‹ค์ œ ๊ฐ์ฒด์™€ ๋™์ผํ•œ ๋ชจ์˜ ๊ฐ์ฒด๋ฅผ ๋งŒ๋“ค์–ด ํ…Œ์ŠคํŠธ ํšจ์šฉ์„ฑ์„ ๋†’์ด๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉํ•œ๋‹ค.

๋ชจ์˜ ๊ฐ์ฒด(Mock Object)๋ž€ ์ฃผ๋กœ ๊ฐ์ฒด ์ง€ํ–ฅ ํ”„๋กœ๊ทธ๋ž˜๋ฐ์œผ๋กœ ๊ฐœ๋ฐœํ•œ ํ”„๋กœ๊ทธ๋žจ์„ ํ…Œ์ŠคํŠธ ํ•  ๊ฒฝ์šฐ ํ…Œ์ŠคํŠธ๋ฅผ ์ˆ˜ํ–‰ํ•  ๋ชจ๋“ˆ๊ณผ ์—ฐ๊ฒฐ๋˜๋Š” ์™ธ๋ถ€์˜ ๋‹ค๋ฅธ ์„œ๋น„์Šค๋‚˜ ๋ชจ๋“ˆ๋“ค์„ ์‹ค์ œ ์‚ฌ์šฉํ•˜๋Š” ๋ชจ๋“ˆ์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ  ์‹ค์ œ์˜ ๋ชจ๋“ˆ์„ "ํ‰๋‚ด"๋‚ด๋Š” "๊ฐ€์งœ" ๋ชจ๋“ˆ์„ ์ž‘์„ฑํ•˜์—ฌ ํ…Œ์ŠคํŠธ์˜ ํšจ์šฉ์„ฑ์„ ๋†’์ด๋Š”๋ฐ ์‚ฌ์šฉํ•˜๋Š” ๊ฐ์ฒด์ด๋‹ค.

์œ„ํ‚ค ๋ฐฑ๊ณผ-๋ชจ์˜ ๊ฐ์ฒด

Mockito๋Š” Mock ๊ฐ์ฒด๋ฅผ ์ง€์›ํ•˜๋Š” ํ…Œ์ŠคํŠธ ํ”„๋ ˆ์ž„์›Œํฌ๋กœ, Mockito๋ฅผ ํ™œ์šฉํ•˜์—ฌ ๊ฐ€์งœ ๊ฐ์ฒด์— ์›ํ•˜๋Š” ๊ฒฐ๊ณผ๋ฅผ Stubํ•˜์—ฌ ๋‹จ์œ„ ํ…Œ์ŠคํŠธ๋ฅผ ์ง„ํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค.

ํ™˜๊ฒฝ ์„ค์ •

์Šคํ”„๋ง ๋ถ€ํŠธ 2.0์ดํ›„์—๋Š” spring-boot-starter-test dependency์— ํฌํ•จ๋˜์–ด ์žˆ๋‹ค.

image-20211226210215502

gradle

repositories { mavenCentral() }
dependencies { testImplementation "org.mockito:mockito-core:4.0.0" }

์‹œ์ž‘ํ•˜๊ธฐ

๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์„ค์ • ํ›„ Mockito๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ํ…Œ์ŠคํŠธ ํด๋ž˜์Šค ์œ„์— @ExtendWith(MockitoExtension.class)๋ฅผ ์ถ”๊ฐ€ํ•ด์ค˜์•ผํ•œ๋‹ค.

@ExtendWith(MockitoExtension.class)
public class mockTest {
}

Mock ์ƒ์„ฑ

mock ์ƒ์„ฑ๊ณผ ๊ด€๋ จ๋œ ์–ด๋…ธํ…Œ์ด์…˜์€ @Mock, @Spy, @InjectMock์ด ์žˆ๋‹ค.

mock ์ƒ์„ฑ ๊ด€๋ จ ํ…Œ์ŠคํŠธ๋ฅผ ์œ„ํ•ด ์ž„์˜๋กœ ItemService๋ฅผ ์ƒ์„ฑํ•ด์ฃผ์—ˆ๋‹ค.

public class ItemService {

    public Item getItem() {
        return new Item("0000000001", "10");
    }

    public boolean isOnlineMallItem() {
        return true;
    }
}

@Mock

@Mock์€ Mock ๊ฐ์ฒด๋ฅผ ๋งŒ๋“ค์–ด ๋ฐ˜ํ™˜ํ•ด์ฃผ๋Š” ์–ด๋…ธํ…Œ์ด์…˜์ด๋‹ค. ์ƒ์„ฑํ•œ mock ๊ฐ์ฒด๋Š” stubbing์„ ํ•ด์ฃผ์ง€ ์•Š๊ณ , ํ˜ธ์ถœํ•˜๊ฒŒ ๋˜๋ฉด primitive type์€ 0, ์ฐธ์กฐํ˜•์€ null์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

@ExtendWith(MockitoExtension.class)
public class MockTest {
    @Mock
    ItemService itemService;

    @Test
    void ์ฐธ์กฐํƒ€์ž…_NOT_STUBBING_ํ…Œ์ŠคํŠธ() {
        assertThat(itemService.getItem()).isNull();
    }

    @Test
    void ๊ธฐ๋ณธํƒ€์ž…_NOT_STUBBING_ํ…Œ์ŠคํŠธ() {
        assertThat(itemService.isOnlineMallItem()).isFalse();
    }
}
image-20211227173202078

@Spy

@Spy๋กœ ์ƒ์„ฑํ•œ mock ๊ฐ์ฒด๋Š” ์‹ค์ œ ๊ฐ์ฒด์ด๋ฉฐ, ๋ฉ”์†Œ๋“œ ์‹คํ–‰ ์‹œ stubbing์„ ํ•˜์ง€ ์•Š์œผ๋ฉด ๊ธฐ์กด ๋กœ์ง์„ ์‹คํ–‰ํ•œ ๊ฐ’์„, stubbing์„ ํ•œ ๊ฒฝ์šฐ์—๋Š” ๊ทธ ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

@ExtendWith(MockitoExtension.class)
public class SpyTest {

    @Spy
    ItemService itemService;

    @Test
    void NOT_STUBBING_ํ…Œ์ŠคํŠธ() {
        // given

        // when
        Item item = itemService.getItem();

        // then
        assertThat(item).isNotNull();
        assertThat(item.getItemDivCd()).isEqualTo("10");
        assertThat(item.getItemId()).isEqualTo("0000000001");
    }

    @Test
    void STUBBING_ํ…Œ์ŠคํŠธ() {

        // given
        when(itemService.getItem())
                .thenReturn(new Item("0000000002", "20"));

        // when
        Item item = itemService.getItem();

        // then
        assertThat(item).isNotNull();
        assertThat(item.getItemDivCd()).isEqualTo("20");
        assertThat(item.getItemId()).isEqualTo("0000000002");

    }
}
image-20211227173227027

@InjectMocks์€ @Mock ํ˜น์€ @Spy๋กœ ์ƒ์„ฑํ•œ ๊ฐ์ฒด๋ฅผ ์ž๋™์œผ๋กœ ์ฃผ์ž…ํ•ด์ฃผ๋Š” ์–ด๋…ธํ…Œ์ด์…˜์ด๋‹ค. ํ•ด๋‹น ์–ด๋…ธํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•ด ์ฃผ์ž…๋ฐ›์„ ์ˆ˜ ์žˆ๋Š” ํ˜•ํƒœ๋Š” Constructure, Property Setter, Field Injection์ด ์žˆ๋‹ค.

Stub

Stub(Stubbing)์€ ํ† ๋ง‰, ๋‚จ์€ ๋ถ€๋ถ„, ๊ฝ์ดˆ, ๋ชฝ๋‹น์—ฐํ•„์ด๋ผ๋Š” ์˜๋ฏธ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์œผ๋ฉฐ ๊ฐ€์งœ ๊ฐ์ฒด๊ฐ€ ๋งˆ์น˜ ์‹ค์ œ๋กœ ๋™์ž‘ํ•˜๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ๋ณด์ด๋„๋ก ๋งŒ๋“ค์–ด๋†“์€ ๊ฒƒ์ด๋‹ค.

Test stubs provide canned answers to calls made during the test, usually not responding at all to anything outside whatโ€™s programmed in for the test.

wikipedia

์ฆ‰, stub์€ ๋งŒ๋“ค์–ด์ง„ mock ๊ฐ์ฒด๊ฐ€ ์–ด๋–ค ๊ฐ’์„ ๋ฐ˜ํ™˜ํ• ์ง€๋ฅผ ์ •์˜ํ•˜๋Š” ๊ฒƒ์ด๋‹ค.

OngoingStubbing

Mockito์—์„œ๋Š” when ๋ฉ”์†Œ๋“œ๋ฅผ ์ด์šฉํ•ด OngoingStubbing์„ ์ง€์›ํ•˜๊ณ  ์žˆ๋‹ค. OngoingStubbing์€ when์— ๋„ฃ์€ ๋ฉ”์†Œ๋“œ์˜ ๋ฆฌํ„ด๊ฐ’์„ ์ •์˜ํ•ด์ฃผ๋Š” ๋ฉ”์†Œ๋“œ์ด๋‹ค.

๋ฉ”์†Œ๋“œ๋ช…
์„ค๋ช…

thenReturn

Stubbingํ•œ ๋ฉ”์†Œ๋“œ ํ˜ธ์ถœ ํ›„ ์–ด๋–ค ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ• ์ง€ ์ •์˜

thenThrow

Stubbingํ•œ ๋ฉ”์†Œ๋“œ ํ˜ธ์ถœ ํ›„ ์–ด๋–ค Exception์„ Throwํ• ์ง€ ์ •์˜

thenAnswer

Stubbingํ•œ ๋ฉ”์†Œ๋“œ ํ˜ธ์ถœ ํ›„ ์–ด๋–ค ์ž‘์—…์„ ํ• ์ง€ custom ์ •์˜ ์ด ๋ฉ”์†Œ๋“œ๋Š” ์‚ฌ์šฉํ•˜์ง€ ๋ง๊ณ , thenReturn, thenThrow ์‚ฌ์šฉ ๊ถŒ์žฅ

thenCallRealMethod

์‹ค์ œ ๋ฉ”์†Œ๋“œ ํ˜ธ์ถœ

when({stubbingํ•  ๋ฉ”์†Œ๋“œ}).{OngoingStubbing ๋ฉ”์†Œ๋“œ};
@ExtendWith(MockitoExtension.class)
public class OngoingStubbingTest {

    @Mock
    ItemService itemService;

    @Test
    void thenReturn_ํ…Œ์ŠคํŠธ() {
        Item item = new Item("0000000002", "20");
        when(itemService.getItem())
                .thenReturn(item);

        assertThat(itemService.getItem()).isEqualTo(item);
    }


    @Test
    void thenThrow_ํ…Œ์ŠคํŠธ() {
        when(itemService.getItem())
                .thenThrow(new IllegalArgumentException());

        assertThrows(IllegalArgumentException.class, () -> {
            itemService.getItem();
        });
    }

    @Test
    void thenCallRealMethod_ํ…Œ์ŠคํŠธ() {
        when(itemService.getItem())
                .thenCallRealMethod();

        assertThat(itemService.getItem()).isNotNull();
        assertThat(itemService.getItem().getItemId()).isEqualTo("0000000001");
    }
}
image-20211227180909081

๋ฉ”์†Œ๋“œ ์ฒด์ด๋‹์œผ๋กœ ์‚ฌ์šฉํ•˜๋ฉด, ํ˜ธ์ถœ์‹œ๋งˆ๋‹ค ๋‹ค๋ฅธ Stubbing์„ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ๋‹ค.

    @Test
    void ๋ฉ”์†Œ๋“œ์ฒด์ด๋‹_ํ…Œ์ŠคํŠธ() {
        Item item = new Item("0000000002", "20");
        when(itemService.getItem())
                .thenReturn(item)
                .thenThrow(new RuntimeException());

        assertThat(itemService.getItem()).isEqualTo(item);

        assertThrows(RuntimeException.class, () -> {
            itemService.getItem();
        });
    }
image-20211227182750720

Stubber

Stubber ๋ฉ”์†Œ๋“œ๋Š” ๋ฐ˜๋“œ์‹œ Stubbing์ด ์‹คํ–‰๋˜์•ผํ•˜๋Š” ๊ฒฝ์šฐ ์‚ฌ์šฉํ•˜๋Š” ๋ฉ”์†Œ๋“œ์ด๋‹ค.

๋ฉ”์†Œ๋“œ๋ช…

์„ค๋ช…

doReturn

Stubbing ๋ฉ”์†Œ๋“œ ํ˜ธ์ถœ ํ›„ ์–ด๋–ค ํ–‰๋™์„ ํ•  ๊ฑด์ง€ ์ •์˜

doThrow

Stubbing ๋ฉ”์†Œ๋“œ ํ˜ธ์ถœ ํ›„ ์–ด๋–ค Exception์„ throwํ•  ๊ฑด์ง€ ์ •์˜

doAnswer

Stubbing ๋ฉ”์†Œ๋“œ ํ˜ธ์ถœ ํ›„ ์ž‘์—…์„ ํ• ์ง€ customํ•˜๊ฒŒ ์ •์˜

doNothing

Stubbing ๋ฉ”์†Œ๋“œ ํ˜ธ์ถœ ํ›„ ์–ด๋–ค ํ–‰๋™๋„ ํ•˜์ง€ ์•Š๊ฒŒ ์ •์˜

doCallRealMethod

์‹ค์ œ ๋ฉ”์†Œ๋“œ ํ˜ธ์ถœ

{Stubber ๋ฉ”์†Œ๋“œ}.when({Stubbingํ•  ํด๋ž˜์Šค}).{Stubbing ํ•  ๋ฉ”์†Œ๋“œ}
@ExtendWith(MockitoExtension.class)
public class StubberTest {

    @Mock
    ItemService itemService;


    @Test
    void doReturn_ํ…Œ์ŠคํŠธ() {
        List list = new LinkedList();

        List spyList = spy(list);

        doReturn("test").when(spyList).get(0);

        assertThat(spyList.get(0)).isEqualTo("test");

        Item item = new Item("0000000003", "30");
        doReturn(item)
                .when(itemService)
                .getItem();

        assertThat(itemService.getItem()).isEqualTo(item);
    }

    @Test
    void doThrow_ํ…Œ์ŠคํŠธ() {
        doThrow(new RuntimeException()).when(itemService).getItem();

        assertThatThrownBy(() -> itemService.getItem()).isInstanceOf(RuntimeException.class);
        assertThrows(RuntimeException.class, () -> {
            itemService.getItem();
        });
    }
}
image-20211227182825006

๊ฒ€์ฆ

verify() ๋ฅผ ์ด์šฉํ•˜์—ฌ Stubbingํ•œ ๋ฉ”์†Œ๋“œ๊ฐ€ ์ •์ƒ์ ์œผ๋กœ ์‹คํ–‰๋๋Š”์ง€ ๊ฒ€์ฆํ•  ์ˆ˜ ์žˆ๋‹ค.

verify(T mock, VerificationMode mode)
๋ฉ”์†Œ๋“œ๋ช…
์„ค๋ช…

times(n)

๋ช‡ ๋ฒˆ์ด ํ˜ธ์ถœ๋๋Š”์ง€ ๊ฒ€์ฆ

never

ํ•œ ๋ฒˆ๋„ ํ˜ธ์ถœ๋˜์ง€ ์•Š์•˜๋Š”์ง€ ๊ฒ€์ฆ

atLeastOne

์ตœ์†Œ ํ•œ ๋ฒˆ์€ ํ˜ธ์ถœ๋๋Š”์ง€ ๊ฒ€์ฆ

atLeast(n)

์ตœ์†Œ n ๋ฒˆ์ด ํ˜ธ์ถœ๋๋Š”์ง€ ๊ฒ€์ฆ

atMostOnce

์ตœ๋Œ€ ํ•œ ๋ฒˆ์ด ํ˜ธ์ถœ๋๋Š”์ง€ ๊ฒ€์ฆ

atMost(n)

์ตœ๋Œ€ n ๋ฒˆ์ด ํ˜ธ์ถœ๋๋Š”์ง€ ๊ฒ€์ฆ

calls(n)

n๋ฒˆ์ด ํ˜ธ์ถœ๋๋Š”์ง€ ๊ฒ€์ฆ (InOrder๋ž‘ ๊ฐ™์ด ์‚ฌ์šฉํ•ด์•ผ ํ•จ)

only

ํ•ด๋‹น ๊ฒ€์ฆ ๋ฉ”์†Œ๋“œ๋งŒ ์‹คํ–‰๋๋Š”์ง€ ๊ฒ€์ฆ

timeout(long mills)

n ms ์ด์ƒ ๊ฑธ๋ฆฌ๋ฉด Fail ๊ทธ๋ฆฌ๊ณ  ๋ฐ”๋กœ ๊ฒ€์ฆ ์ข…๋ฃŒ

after(long mills)

n ms ์ด์ƒ ๊ฑธ๋ฆฌ๋Š”์ง€ ํ™•์ธ timeout๊ณผ ๋‹ค๋ฅด๊ฒŒ ์‹œ๊ฐ„์ด ์ง€๋‚˜๋„ ๋ฐ”๋กœ ๊ฒ€์ฆ ์ข…๋ฃŒ๊ฐ€ ๋˜์ง€ ์•Š์Œ

description

์‹คํŒจํ•œ ๊ฒฝ์šฐ ๋‚˜์˜ฌ ๋ฌธ๊ตฌ

@ExtendWith(MockitoExtension.class)
public class VerifyTest {

    @Mock
    ItemService itemService;

    @Test
    void times_ํ…Œ์ŠคํŠธ() {
        itemService.getItem();
        itemService.getItem();

        // ๋ช‡๋ฒˆ ํ˜ธ์ถœ๋๋Š”์ง€ ๊ฒ€์ฆ
        verify(itemService, times(2)).getItem();
    }

    @Test
    void never_ํ…Œ์ŠคํŠธ() {

        // ํ•œ ๋ฒˆ๋„ ํ˜ธ์ถœ๋˜์ง€ ์•Š์•˜๋Š”์ง€ ๊ฒ€์ฆ
        verify(itemService, never()).getItem();
    }

    @Test
    void atLeastOnce_ํ…Œ์ŠคํŠธ() {

        itemService.getItem();
        itemService.getItem();
        itemService.getItem();
        itemService.getItem();

        // ์ตœ์†Œ 1๋ฒˆ ํ˜ธ์ถœ๋๋Š”์ง€ ๊ฒ€์ฆ
        verify(itemService, atLeastOnce()).getItem();
    }

    @Test
    void atLeast_ํ…Œ์ŠคํŠธ() {

        itemService.getItem();
        itemService.getItem();
        itemService.getItem();
        itemService.getItem();

        // ์ตœ์†Œ N๋ฒˆ ํ˜ธ์ถœ๋๋Š”์ง€ ๊ฒ€์ฆ
        verify(itemService, atLeast(4)).getItem();
    }

    @Test
    void atMostOnce_ํ…Œ์ŠคํŠธ() {

        itemService.getItem();

        // ์ตœ๋Œ€ N๋ฒˆ ํ˜ธ์ถœ๋๋Š”์ง€ ๊ฒ€์ฆ
        verify(itemService, atMostOnce()).getItem();
    }

    @Test
    void atMost_ํ…Œ์ŠคํŠธ() {

        itemService.getItem();
        itemService.getItem();
        itemService.getItem();
        itemService.getItem();

        // ์ตœ๋Œ€ N๋ฒˆ ํ˜ธ์ถœ๋๋Š”์ง€ ๊ฒ€์ฆ
        verify(itemService, atMost(4)).getItem();
    }
    
    @Test
    void inOrder_calls_ํ…Œ์ŠคํŠธ() {
        itemService.getItem();
        itemService.getItem();
        itemService.getItem();
        itemService.deleteItem();

        InOrder inOrder = inOrder(itemService);

        inOrder.verify(itemService, calls(3)).getItem();
        inOrder.verify(itemService).deleteItem();
    }

}

์ฐธ๊ณ 

Last updated

Was this helpful?