본문 바로가기

TDD

Mockito 학습 테스트

이 글은 Junit5를 기준으로 작성되었습니다.

0. Mockito란?

가짜 객체인 Mock을 만들어 테스트를 원활하게 도와주는 테스트 프레임워크.

스터빙으로 Mock을 조작합니다.

💡 스터빙(Stubbing) : Mock 객체의 행동을 조작하여 원하는 결과를 반환하도록 하는 행위

 

1. Mock 학습 테스트

1.1 mock

mock을 이용해 Mock 객체를 만들 수 있습니다.

import static org.mockito.Mockito.*;

List<String> mocks = mock(ArrayList.class);

💡 mock은 가짜 객체이므로, mock을 통해 만들어진 객체의 메소드는 동작하지 않습니다.

 

1.2 when

when을 사용해 행동에서, thenReturn을 사용해 값을 리턴하도록 만들 수 있습니다.

이를 스터빙이라 합니다.

아래 코드는 get(0)를 호출하면, ksb를 반환하라는 의미입니다.

@Test
public void whenThenReturnTest(){
    List<String> mocks = mock(ArrayList.class);
    String givenValue = mocks.get(0);

    when(givenValue).thenReturn("ksb");

    assertEquals("ksb", mocks.get(0));
}

예외 처리가 필요할 경우에, when-thenThrow로 해결할 수 있습니다.

@Test
public void whenThenThrowTest(){
    List<String> mocks = mock(ArrayList.class);

    mocks.add(null);

    when(mocks.get(0))
            .thenThrow(NullPointerException.class);
}

 

1.3 any

일정한 반환 값을 인자와 무관하게 만들고 싶을 때 사용합니다.

anyInt(), anyString(), anyLong() 등이 있습니다.

@Test
public void anyTest(){
    List<String> mocks = mock(ArrayList.class);

    String givenValue = mocks.get(anyInt());

    when(givenValue).thenReturn("ksb");

    assertEquals("ksb", mocks.get(0));
    assertEquals("ksb", mocks.get(1));
    assertEquals("ksb", mocks.get(10_000_000));
}

 

1.4 verify

mock 객체에서 어떤 메소드가 호출되었는지 알고 싶을 때 사용합니다.

아래 코드로 List Mock 객체에서 add()가 호출 되었다는 것을 검증합니다.

@Test
public void verifyTest(){
    List<String> mocks = mock(ArrayList.class);

    mocks.add("ksb");

    verify(mocks).add("ksb");
}

 

1.5 times

mock 개체에서 메소드가 몇 번 호출되었는지 확인할 때 사용합니다.

아래 코드로 add()가 두 번 호출된 것을 검증합니다.

@Test
public void verifyTimeTest(){
    List<String> mocks = mock(ArrayList.class);

    mocks.add("ksb");
    mocks.add("ksb2");

    verify(mocks, times(2)).add(anyString());
}

atLeast(), atLeastOnce()로 최소로 호출되는 횟수를 검증할 수 있습니다.

@Test
public void verifyAtLeastAndAtLeastOnceTest(){
    List<String> mocks = mock(ArrayList.class);

    mocks.add("ksb");
    mocks.add("ksb2");
    mocks.add("ksb3");
    mocks.add("ksb4");

    verify(mocks, atLeast(2)).add(anyString());
    verify(mocks, atLeastOnce()).add(anyString());
}

 

1.6 argumentCaptor

mock 객체에 전달된 인자를 확인할 때 사용합니다.

verify()에 capture()를 전달하고, getValue()로 전달된 인자를 확인할 수 있습니다.

@Test
public void argumentCaptorTest(){
    List<String> mocks = mock(ArrayList.class);
    ArgumentCaptor<String> arg = ArgumentCaptor.forClass(String.class);

    mocks.add("ksb");
    verify(mocks).add(arg.capture());

    assertEquals("ksb", arg.getValue());
}

 

2. Spy 학습 테스트

2.1 spy

spy를 통해 만들어진 객체는 실제 객체처럼 동작합니다.

즉, mock과 달리 내부 메소드가 동작됩니다.

@Test
public void spyingEqualTest(){
    List<String> spies = spy(ArrayList.class);

    spies.add("ksb");
    assertEquals("ksb", spies.get(0));
}

 

2.2 verify

mock과 동일하게 verify로 호출된 메소드를 검증할 수 있습니다.

@Test
public void spyingVerifyTest(){
    List<String> spies = spy(ArrayList.class);

    spies.add("ksb");
    verify(spies).add("ksb");

    spies.get(0);
    verify(spies).get(0);
}

 

2.3 exception

spy는 mock과 달리 실제 메소드가 동작됩니다.

@Test
public void spyingIndexOutOfBoundsExceptionTest(){
    List<String> spies = spy(ArrayList.class);

    assertThrows(IndexOutOfBoundsException.class, () -> {
        when(spies.get(0)).thenReturn("ksb");
    });
}

때문에, 위 코드에서 spies.get(0)는 내부에 아무런 값이 저장되어 있지 않아 IndexOutOfBoundsException가 발생합니다.

 

2.4 doReturn-when

2.3에서 발생하는 spy 함수 호출 문제를 doReturn-when으로 해결할 수 있습니다.

@Test
public void spyingDoReturnTest(){
    List<String> spies = spy(ArrayList.class);

    doReturn("ksb").when(spies).get(0);
    doReturn("ksb1").when(spies).get(1);

    assertEquals("ksb", spies.get(0));
    assertEquals("ksb1", spies.get(1));
}