Mockito 정리 (1)

· β˜• 4 min read · πŸ‘€... views

Mockito λž€?

  • μžλ°”μ—μ„œ λ‹¨μœ„ ν…ŒμŠ€νŠΈλ₯Ό μœ„ν•œ Mocking framework
  • tastes really good(?!)
  • (개인적으둠) κ°„νŽΈν•˜κ²Œ μ‚¬μš©ν•  수 μžˆμ–΄μ„œ ν…ŒμŠ€νŠΈμ½”λ“œ μž‘μ„±ν•  λ•Œ 즐겨 μ‚¬μš©ν•˜λŠ” νŽΈμž…λ‹ˆλ‹€.

1. μ˜μ‘΄μ„± μΆ”κ°€

1
2
3
4
5
6
<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-core</artifactId>
    <version>3.0.0</version>
    <scope>test</scope>
</dependency>
  • 버전에 λŒ€ν•΄
    • mockito 의 버전은 1.x, 2.x, 3.x 둜 총 3κ°œκ°€ μžˆμŠ΅λ‹ˆλ‹€. 곡식 wiki 에 λ”°λ₯΄λ©΄ 1κ³Ό 2λŠ” λ§žμ§€ μ•ŠλŠ”(Incompatible) 것도 μžˆλ‹€κ³  ν•©λ‹ˆλ‹€. 2와 3은 API λ‘œλŠ” λ³€κ²½λœ 것이 μ—†μœΌλ‚˜, Java8 에 λ§žμΆ°μ‘Œλ‹€κ³  ν•©λ‹ˆλ‹€.

2. μ‚¬μš© 방법

2-1. verify

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
// mock λ“±μ˜ μ½”λ“œλ₯Ό κΉ”λ”ν•˜κ²Œ ν•˜κΈ° μœ„ν•΄ static import
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;

public class MockitoTest {
    @Test
    public void verify_ν…ŒμŠ€νŠΈ() {
        //mock 객체 생성
        List mockedList = mock(List.class);

        mockedList.add("one");
        mockedList.clear();
        
		// 검증
        verify(mockedList).clear();
        verify(mockedList).add("one");
        verify(mockedList).add("two"); // 검증 μ‹€νŒ¨ (호좜된 적이 μ—†κΈ° λ•Œλ¬Έμ—)
    }
}
  • μƒμ„±λ˜λ©΄ mock 은 λͺ¨λ“  μƒν˜Έμž‘μš©μ„ κΈ°μ–΅ν•©λ‹ˆλ‹€. μ–΄λ–€ λ©”μ†Œλ“œκ°€ μ‹€ν–‰λ˜μ—ˆλŠ”μ§€ verify 둜 검증할 수 있으며, κ²€μ¦μ—λŠ” μˆœμ„œλ₯Ό 따지지 μ•ŠλŠ” 것을 확인할 수 μžˆμŠ΅λ‹ˆλ‹€.

2-2. stubbing

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
public void stubbing_ν…ŒμŠ€νŠΈ() {

    LinkedList mockedList = mock(LinkedList.class);
    
    // stubbing
    when(mockedList.get(0)).thenReturn("first");
    when(mockedList.get(1)).thenThrow(new RuntimeException("not bound index"));

    // 첫번째 first 호좜
    System.out.println(mockedList.get(0)); // first

    // stub 해주지 μ•Šμ•˜μœΌλ―€λ‘œ null 호좜
    System.out.println(mockedList.get(999)); // null

    // runtimeException λ°œμƒ
    System.out.println(mockedList.get(1)); // java.lang.RuntimeException: not bound index
}
  • stub λΌλŠ” λ‹¨μ–΄λŠ” ν•œκΈ€λ‘œ ν•΄μ„ν•˜λ©΄ 그루터기 λΌλŠ” 뜻인데, λͺ¨λ₯΄κ² μ‹Άμ–΄ κ΄€λ ¨λ‚΄μš©μ„ wiki μ—μ„œ 찾아보면 Test Stub 이 μžˆλŠ”λ°μš”. λ‚΄μš©μ„ 보면

    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.

    μ—¬κΈ°μ„œ λ‚˜μ˜€λŠ” canned answer μ΄λΌλŠ” 건 정해진 λ‹΅ 으둜 stubbing μ΄λž€ 정해진 응닡을 주도둝 ν•˜λŠ” 것이라 λ³Ό 수 μžˆκ² μŠ΅λ‹ˆλ‹€. (자꼬치기?)

    2-3. Argument matchers

1
2
3
4
5
6
7
8
9
@Test
public void argument_matchers_ν…ŒμŠ€νŠΈ() {

    LinkedList mockedList = mock(LinkedList.class);
    
    when(mockedList.get(anyInt())).thenReturn("element");

    System.out.println(mockedList.get(999));
}
  • stubbing 을 ν•  λ•Œ μž…λ ₯ νŒŒλΌλ―Έν„° 인자λ₯Ό μœ μ—°ν•˜κ²Œ ν•˜κ³  싢을 λ•Œ μ‚¬μš©ν•©λ‹ˆλ‹€.
  • argument matchers 의 anyX() λ©”μ†Œλ“œλ“€μ€ ν…ŒμŠ€νŠΈλ₯Ό νŽΈν•˜κ²Œ 도와주긴 ν•˜μ§€λ§Œ, 경함상 Int νŒŒλΌλ―Έν„°κ°€ ν•„μš”ν•œ μžλ¦¬μ— any() λ₯Ό μ“°λ©΄ μ—λŸ¬κ°€ λ‚¬λ˜ 걸둜 κΈ°μ–΅ν•©λ‹ˆλ‹€. (μ œλ„€λ¦­μΈμ€„ μ•Œμ•˜λŠ”λ° 말이죠.)
  • arugment matcherλ₯Ό μ‚¬μš©ν•˜λ €λ©΄ λͺ¨λ“  νŒŒλΌλ―Έν„°λŠ” matcher 둜 μ „λ‹¬λ˜μ–΄μ•Ό ν•©λ‹ˆλ‹€. 즉, String λ¬Έμžμ—΄μ„ 넣을 땐 eq() 둜 감싸주어야 ν•©λ‹ˆλ‹€.
1
2
when(someMethod.call(anyInt(), "url")).thenReturn("OK"); // error
when(someMethod.call(anyInt(), eq("url"))).thenReturn("OK"); // 정상 λ™μž‘

2-4. 호좜 여뢀에 λŒ€ν•œ 검증

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@Test
public void times_κ²€μ¦ν…ŒμŠ€νŠΈ() {
    LinkedList mockedList = mock(LinkedList.class);

    mockedList.add("once");

    mockedList.add("twice");
    mockedList.add("twice");

    mockedList.add("three times");
    mockedList.add("three times");
    mockedList.add("three times");

    // μ§€μ •ν•œ 횟수만큼 ν˜ΈμΆœλ˜μ—ˆλŠ”μ§€ 검사
    verify(mockedList, times(1)).add("once");
    verify(mockedList, times(2)).add("twice");
    verify(mockedList, times(3)).add("three times");

    // never() == times(0)
    verify(mockedList, never()).add("never");

    verify(mockedList, atLeastOnce()).add("three times");
    verify(mockedList, atLeast(2)).add("six times"); // times(0) 검증 μ‹€νŒ¨
    verify(mockedList, atMost(5)).add("three times");
}
  • λ©”μ†Œλ“œ 호좜 νšŸμˆ˜μ™€ 호좜 μ—¬λΆ€λ₯Ό 확인할 수 μžˆμŠ΅λ‹ˆλ‹€. times(1) 의 κ²½μš°λŠ” default μ΄λ―€λ‘œ μƒλž΅ κ°€λŠ₯ν•©λ‹ˆλ‹€.

2-5. void method with exception

1
2
3
4
5
6
7
8
@Test
public void void_exception_ν…ŒμŠ€νŠΈ() {
    LinkedList mockedList = mock(LinkedList.class);

    doThrow(new RuntimeException()).when(mockedList).clear();

    mockedList.clear(); // runtimeException
}
  • void λ©”μ†Œλ“œμ—μ„œ exception 을 던질 λ•Œ μ‚¬μš©ν•©λ‹ˆλ‹€.

2-6. μˆœμ„œ κ²€μ¦ν•˜κΈ°

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
@Test
public void μˆœμ„œκ²€μ¦ν•˜κΈ°_ν…ŒμŠ€νŠΈ() {
    List firstMock = mock(List.class);
    List secondMock = mock(List.class);

    firstMock.add("first call");
    secondMock.add("second call");

    InOrder inOrder = inOrder(firstMock, secondMock);

    inOrder.verify(firstMock).add("first call");
    inOrder.verify(secondMock).add("second call");
}
  • mock 듀이 μˆœμ„œλŒ€λ‘œ μ‹€ν–‰λ˜λŠ”μ§€ κ²€μ¦ν•˜λŠ” λ°©μ‹μž…λ‹ˆλ‹€.
  • inOrder 에 λ“€μ–΄κ°€λŠ” μˆœμ„œλŠ” μƒκ΄€μ—†λ„€μš”. verify λ₯Ό 톡해 μˆœμ„œλŒ€λ‘œ μ‹€ν–‰λ˜λŠ”μ§€ 확인할 수 μžˆμŠ΅λ‹ˆλ‹€.

2-7. 연속적인 콜 stubbing

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
@Test
public void 연속적인_콜_stubbing() {
    MockingExample mockingExample = mock(MockingExample.class);
    when(mockingExample.someMethod(eq("some arg"))).thenThrow(new RuntimeException()).thenReturn("foo");

    // 첫번째 호좜 runtime Exception
    mockingExample.someMethod("some arg");
    // 'foo' 호좜
    System.out.println(mockingExample.someMethod("some arg"));
    // 이후 λͺ¨λ“  ν˜ΈμΆœμ€ λ§ˆμ§€λ§‰ stubbing 인 'foo' 호좜
    System.out.println(mockingExample.someMethod("some arg"));
    // λ‹€μŒκ³Ό 같이 κ°„λž΅ν™” κ°€λŠ₯
    when(mockingExample.someMethod(eq("some arg"))).thenReturn("one", "two", "three");

}
  • ν•΄λ‹Ή λ©”μ†Œλ“œλ₯Ό ν˜ΈμΆœν•  λ•Œλ§ˆλ‹€ λ‹€λ₯Έ return 값을 보여주기 μœ„ν•΄ μ‚¬μš©ν•©λ‹ˆλ‹€.
  • 이런 κ²½μš°λŠ” 거의 μ—†λ‹€κ³  ν•˜μ§€λ§Œ, μ•Œμ•„λ‘λŠ” 것도 λ‚˜μ˜μ§€ μ•Šμ„ 것 κ°™λ„€μš”.

2-8. μ‹€μ œ 객체 κ°μ‹œν•˜κΈ°

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
@Test
public void spy_ν…ŒμŠ€νŠΈ() {
    List list = new LinkedList();
    List spy = spy(list);
    // νŠΉμ • λ©”μ†Œλ“œ stub 
    when(spy.size()).thenReturn(100);
    // real method 호좜
    spy.add("one");
    spy.add("two");

    System.out.println(spy.get(0));
    // stub 된 size() κ°€ 호좜
    System.out.println(spy.size());

    verify(spy).add("one");
    verify(spy).add("two");
}
  • real 객체에 λŒ€ν•΄ κ°μ‹œν•  수 μžˆλ‹€κ³  ν•©λ‹ˆλ‹€. method κ°€ stub λ˜μ§€ μ•Šμ•˜μ„ 경우 μ‹€μ œ method κ°€ 호좜이 λ©λ‹ˆλ‹€. μœ„μ˜ κ²½μš°μ—μ„œλŠ” size() 에 λŒ€ν•΄μ„œλ§Œ stub 을 ν–ˆμŠ΅λ‹ˆλ‹€. 자주 μ‚¬μš©ν•˜μ§€ μ•Šμ•„μ„œ κ·ΈλŸ°μ§€ μ•Œμ­λ‹¬μ­ ν•©λ‹ˆλ‹€.
  • “partial mocking” μ΄λΌλŠ” κ°œλ…κ³Ό μ—°κ΄€λ˜μ–΄ μžˆλ‹€κ³  ν•©λ‹ˆλ‹€.
1
2
3
4
5
6
7
8
List list = new LinkedList();
List spy = spy(list);
   
// spy.get(0)은 listκ°€ 아직 λΉ„μ–΄μžˆμœΌλ―€λ‘œ IndexOutOfBoundsException
when(spy.get(0)).thenReturn("foo");
   
// doReturn을 μ΄μš©ν•΄ stubbing
doReturn("foo").when(spy).get(0);
  • λΆ€λΆ„λ§Œ mocking ν•œλ‹€λŠ” 건 이런 문제점이 λ°œμƒν•  수 μžˆκΈ°μ— 잘 써야 ν•  것 κ°™λ„€μš”.

2-9. stubbing λ˜μ§€ μ•Šμ€ λ©”μ†Œλ“œμ— κΈ°λ³Έ 리턴값 μ„€μ •

1
2
3
4
5
@Test
public void stubbing_κΈ°λ³Έκ°’_μ„€μ •_ν…ŒμŠ€νŠΈ() {
    MockingExample mock = mock(MockingExample.class, Mockito.RETURNS_SMART_NULLS);
    System.out.println(mock.someMethod("test")); // ""
}
  • stub λ˜μ§€ μ•Šμ€ method λ₯Ό ν˜ΈμΆœν•  λ•Œ λ¦¬ν„΄λ˜λŠ” 값을 정해쀄 수 μžˆμŠ΅λ‹ˆλ‹€.
  • 쒋은 ν…ŒμŠ€νŠΈλ₯Ό μœ„ν•΄ κΌ­ ν•„μš”ν•œ κΈ°λŠ₯은 μ•„λ‹ˆμ§€λ§Œ, λ ˆκ±°μ‹œ μ‹œμŠ€ν…œμ—λŠ” μœ μš©ν•˜κ²Œ μ‚¬μš©λ  수 μžˆλ‹€κ³  ν•©λ‹ˆλ‹€..만, 어디에 μ‚¬μš©ν• μ§€ λ”± ν•˜κ³  λŠλ‚Œμ΄ μ˜€μ§„ μ•Šλ„€μš”.

2-10. Aliases for Behavior Driven Development

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
public void shouldBuyBread() throws Exception {
    //given
    given(seller.askForBread()).willReturn(new Bread());
    
    //when
    Goods goods = shop.buyBread();
    
    //then
    then(goods).should().containBread();
}
  • BDD μŠ€νƒ€μΌμ˜ ν…ŒμŠ€νŠΈ μž‘μ„± 방법은 ν…ŒμŠ€νŠΈ method 에 기본으둜 //given, //when, //then 이라고 주석을 λ‹¬μ•„μ£ΌλŠ” 것이라 ν•©λ‹ˆλ‹€. (저도 λͺ¨λ₯΄κ²Œ BDD 방식을 μ‚¬μš©ν•΄ ν…ŒμŠ€νŠΈ μ½”λ“œλ₯Ό μž‘μ„±ν•˜κ³  μžˆμ—ˆλ„€μš”.)
  • BDDMockito.given(Object) λ₯Ό μ œκ³΅ν•΄ BDD μŠ€νƒ€μΌμ˜ ν…ŒμŠ€νŠΈλ₯Ό λ§Œλ“€ 수 μžˆλ‹€κ³  ν•©λ‹ˆλ‹€.

Reference

Share on

snack
WRITTEN BY
snack
Web Programmer


What's on this Page