Mockito로 void 메소드를 조롱하는 방법


937

void 리턴 타입으로 메소드를 조롱하는 방법?

관찰자 패턴을 구현했지만 방법을 모르기 때문에 Mockito로 조롱 할 수 없습니다.

그리고 인터넷에서 예를 찾으려고했지만 성공하지 못했습니다.

내 수업은 다음과 같습니다.

public class World {

    List<Listener> listeners;

    void addListener(Listener item) {
        listeners.add(item);
    }

    void doAction(Action goal,Object obj) {
        setState("i received");
        goal.doAction(obj);
        setState("i finished");
    }

    private string state;
    //setter getter state
} 

public class WorldTest implements Listener {

    @Test public void word{
    World  w= mock(World.class);
    w.addListener(this);
    ...
    ...

    }
}

interface Listener {
    void doAction();
}

시스템은 모의로 트리거되지 않습니다.

위에서 언급 한 시스템 상태를 보여주고 싶습니다. 그리고 그들에 따라 주장을한다.


6
mock의 void 메소드는 기본적으로 아무 것도 수행하지 않습니다.
라인

1
@ 라인, 내가 찾던 것입니다. 당신이 그것을 말한 후에 분명한 것 같습니다. 그러나 그것은 조롱 원칙을 강조합니다 : 당신은 반환 값이나 예외와 같은 효과를 위해 조롱 된 클래스의 메소드 만 조롱하면됩니다. 감사합니다!
allenjom

답변:


1144

Mockito API 문서를 살펴보십시오 . 링크 된 문서 (포인트의 # 12)를 언급으로 당신이 중 하나를 사용할 수 있습니다 doThrow(), doAnswer(), doNothing(), doReturn()Mockito 프레임 워크의 메서드의 가족은 무효 방법을 조롱합니다.

예를 들어

Mockito.doThrow(new Exception()).when(instance).methodName();

또는 후속 행동과 결합하려면

Mockito.doThrow(new Exception()).doNothing().when(instance).methodName();

setState(String s)World 클래스 에서 setter를 조롱한다고 가정 하면 코드를 사용하여 doAnswer메소드를 조롱하는 것 setState입니다.

World  mockWorld = mock(World.class); 
doAnswer(new Answer<Void>() {
    public Void answer(InvocationOnMock invocation) {
      Object[] args = invocation.getArguments();
      System.out.println("called with arguments: " + Arrays.toString(args));
      return null;
    }
}).when(mockWorld).setState(anyString());

8
@qualidafial : 예, 반환 유형에 관심이 없다는 것을 더 잘 전달하기 때문에 Void에 대한 매개 변수화가 더 좋을 것이라고 생각합니다. 나는이 구조를 알지 못했습니다. 지적 해 주셔서 감사합니다.
sateesh

2
doThrow는 이제 # 5입니다 (또한 doThrow를 사용하면 추종자를 위해 " 'void'type not allowed here"메시지가 수정되었습니다 ...)
rogerdpack

@qualidafial : Answer.answer 호출의 반환 유형은 원래 메서드로 반환되는 것이 아니라 doAnswer 호출로 반환되는 것으로 생각됩니다. 아마도 테스트에서 해당 값으로 다른 작업을 수행하려는 경우입니다.
twelve17

1
:( 구아바에서 RateLimiter.java의 16.0.1 버전을 모의하려고 할 때 doNothing (). w ((mockLimiterReject) .setRate (100)에서 RateLimiter의 setRate를 호출하면 뮤텍스가 한 번 멍청하기 때문에 nullpointer가 발생하므로 nullpointer가 발생 함) 그것은 내 setRate 방법을 조롱 :( 대신 :(를 호출되지 않았다, 그래서
딘 힐러

2
@DeanHiller 통지 setRate()final이므로 조롱 할 수 없습니다. 대신 create()필요한 것을 수행하는 인스턴스를 시도하십시오 . 조롱 할 필요가 없어야한다 RateLimiter.
dimo414

113

나는 그 질문에 대한 더 간단한 대답을 찾았고 하나의 메소드에 대해서만 실제 메소드를 호출하는 것 (공백 리턴이 있더라도)을 수행 할 수 있다고 생각합니다.

Mockito.doCallRealMethod().when(<objectInstance>).<method>();
<objectInstance>.<method>();

또는 해당 클래스의 모든 메소드에 대해 실제 메소드를 호출하여 다음을 수행 할 수 있습니다.

<Object> <objectInstance> = mock(<Object>.class, Mockito.CALLS_REAL_METHODS);

13
이것이 바로 실제 답변입니다. spy () 메서드는 제대로 작동하지만 일반적으로 개체가 대부분의 모든 작업을 수행하려는 경우에 예약되어 있습니다.
biggusjimmus

1
이것은 무엇을 의미 하는가? 실제로 메소드를 호출하고 있습니까? 나는 이전에 mockito를 사용하지 않았습니다.
obesechicken13

그렇습니다. 모의는 실제 메소드를 호출합니다. @Mock을 사용하는 경우 @Mock (answer = Answers.CALLS_REAL_METHODS)를 사용하여 동일한 결과를 얻을 수 있습니다.
Ale

70

@sateesh가 말한 것에 덧붙여서 테스트가 호출되지 않도록 void 메소드를 모방하려는 경우 다음과 같이 사용할 수 있습니다 Spy.

World world = new World();
World spy = Mockito.spy(world);
Mockito.doNothing().when(spy).methodToMock();

테스트를 실행하려면 spy개체가 아닌 개체 에서 테스트중인 메서드를 호출해야 world합니다. 예를 들면 다음과 같습니다.

assertEquals(0,spy.methodToTestThatShouldReturnZero());

57

소위 문제의 해결책은 사용하는 것입니다 spy Mockito.spy (...) 대신 mock Mockito.mock (..)를 .

스파이는 부분 조롱을 가능하게합니다. Mockito는이 문제에 능숙합니다. 완료되지 않은 수업이 있기 때문에이 방법으로이 수업에서 필요한 장소를 조롱합니다.


3
비슷한 문제가 있었기 때문에 여기에서 우연히 발견되었습니다 (우연히도 주제 / 관찰자 상호 작용을 테스트하는 일이 발생했습니다). 이미 스파이를 사용하고 있지만 'SubjectChanged'메서드가 다른 것을 원합니다. `verify (observer) .subjectChanged (subject)를 사용하여 메소드가 호출되었는지 확인할 수 있습니다. 그러나 어떤 이유로 든 메소드를 재정의하는 것이 좋습니다. 이를 위해 Sateesh의 접근 방식과 여기에 대한 귀하의 답변을 결합한 방법이 있습니다.
gMale

32
아니요, 이렇게하면 실제로 void 메소드를 조롱하는 데 도움이되지 않습니다. 비결은 sateesh의 답변에 나열된 네 가지 Mockito 정적 방법 중 하나를 사용하는 것입니다.
Dawood ibn Kareem 2016 년

2
귀하의 질문에 대한 @Gurnard는이 stackoverflow.com/questions/1087339/…를 살펴보십시오 .
ibrahimyilmaz 08

이 선량은 실제로 작동합니다.
Nilotpal

34

우선 : 항상 mockito static을 가져와야합니다.이 방법으로 코드를 훨씬 더 읽기 쉽고 직관적으로 만들 수 있습니다.

import static org.mockito.Mockito.*;

부분적으로 조롱하고 여전히 나머지 기능에 원래 기능을 유지하려면 "스파이"를 제공합니다.

다음과 같이 사용할 수 있습니다.

private World world = spy(World.class);

메소드가 실행되지 않도록하려면 다음과 같이 사용할 수 있습니다.

doNothing().when(someObject).someMethod(anyObject());

메소드에 사용자 정의 동작을 제공하려면 "thenReturn"과 함께 "when"을 사용하십시오.

doReturn("something").when(this.world).someMethod(anyObject());

더 많은 예제를 보려면 문서에서 훌륭한 mockito 샘플을 찾으십시오.


4
정적 가져 오기는 더 읽기 쉽게 만드는 것과 어떤 관련이 있습니까?
jsonbourne

2
말 그대로 아무것도.
specializt

2
나는 그것이 맛의 문제라고 생각합니다. 나는 단지 문장이 영어 문장처럼 보이도록하고 싶습니다.
fl0w

1
팀에서 작업하는 경우 import static은 가져 오기에 와일드 카드가 종종 있기 때문에 다른 사람이 메소드가 어디에서 나오는지 알기 어렵게 만듭니다.
LowKeyEnergy

그러나 테스트 코드와 Mockito의 경우 문제가되지 않아야합니다.
fl0w

27

mockito로 void 메소드를 조롱하는 방법-두 가지 옵션이 있습니다 :

  1. doAnswer -조롱 된 void 메소드가 무언가를하기를 원한다면 (void에도 불구하고 행동을 조롱하십시오).
  2. doThrow- Mockito.doThrow()조롱 된 void 메소드에서 예외를 throw하려는 경우가 있습니다.

다음은 사용 방법의 예입니다 (이상적인 사용 사례는 아니지만 기본 사용법을 보여주고 싶음).

@Test
public void testUpdate() {

    doAnswer(new Answer<Void>() {

        @Override
        public Void answer(InvocationOnMock invocation) throws Throwable {
            Object[] arguments = invocation.getArguments();
            if (arguments != null && arguments.length > 1 && arguments[0] != null && arguments[1] != null) {

                Customer customer = (Customer) arguments[0];
                String email = (String) arguments[1];
                customer.setEmail(email);

            }
            return null;
        }
    }).when(daoMock).updateEmail(any(Customer.class), any(String.class));

    // calling the method under test
    Customer customer = service.changeEmail("old@test.com", "new@test.com");

    //some asserts
    assertThat(customer, is(notNullValue()));
    assertThat(customer.getEmail(), is(equalTo("new@test.com")));

}

@Test(expected = RuntimeException.class)
public void testUpdate_throwsException() {

    doThrow(RuntimeException.class).when(daoMock).updateEmail(any(Customer.class), any(String.class));

    // calling the method under test
    Customer customer = service.changeEmail("old@test.com", "new@test.com");

}
}

내 게시물에서 Mockito로 void 메소드 를 모의 하고 테스트 하는 방법에 대한 자세한 내용은 Mockito 로 모의하는 방법 (예제가있는 포괄적 인 안내서)


1
좋은 예입니다. 참고 : Java 8에서는 익명 클래스 대신 람다를 사용하는 것이 조금 더 좋을 수 있습니다. ()); '
miwe

16

묶음에 다른 답변 추가하기 (말장난 의도 없음) ...

스파이를 사용하지 않으려면 doAnswer 메소드를 호출해야합니다. 그러나 반드시 자신의 Answer 를 굴릴 필요는 없습니다 . 몇 가지 기본 구현이 있습니다. 특히 CallsRealMethods 입니다.

실제로 다음과 같이 보입니다.

doAnswer(new CallsRealMethods()).when(mock)
        .voidMethod(any(SomeParamClass.class));

또는:

doAnswer(Answers.CALLS_REAL_METHODS.get()).when(mock)
        .voidMethod(any(SomeParamClass.class));

16

Java 8에서는 다음에 대한 정적 가져 오기가 있다고 가정하면 약간 더 깔끔해질 수 있습니다 org.mockito.Mockito.doAnswer.

doAnswer(i -> {
  // Do stuff with i.getArguments() here
  return null;
}).when(*mock*).*method*(*methodArguments*);

return null;중요하고 그것을위한 적절한 재정을 찾을 수 없습니다로하지 않고 컴파일은 몇 가지 매우 모호한 오류와 함께 실패합니다 doAnswer.

예를 들어 전달 된 ExecutorService모든 것을 즉시 실행 하는 것은 다음을 사용하여 구현할 수 있습니다.Runnableexecute()

doAnswer(i -> {
  ((Runnable) i.getArguments()[0]).run();
  return null;
}).when(executor).execute(any());

2
한 줄에 : Mockito.doAnswer ((i)-> null) .when (instance) .method (any ());
Akshay Thorve

@AkshayThorve 실제로 i로 작업하고 싶을 때는 작동하지 않습니다.
팀 B

7

문제가 테스트 구조 때문이라고 생각합니다. 테스트 클래스에서 인터페이스를 구현하는 전통적인 방법과 조롱을 혼합하는 것이 어렵다는 것을 알았습니다 (여기에서 한 것처럼).

리스너를 Mock으로 구현하면 상호 작용을 확인할 수 있습니다.

Listener listener = mock(Listener.class);
w.addListener(listener);
world.doAction(..);
verify(listener).doAction();

이것은 '세계'가 옳은 일을하고 있다는 것을 만족시켜야합니다.


0

다음과 같이 Mockito.doThrow 사용 :

Mockito.doThrow(new Exception()).when(instance).methodName();

이 좋은 예를 시도해 볼 수 있습니다.

public void testCloseStreamOnException() throws Exception {
    OutputStream outputStream = Mockito.mock(OutputStream.class);
    IFileOutputStream ifos = new IFileOutputStream(outputStream);
    Mockito.doThrow(new IOException("Dummy Exception")).when(outputStream).flush();
    try {
      ifos.close();
      fail("IOException is not thrown");
    } catch (IOException ioe) {
      assertEquals("Dummy Exception", ioe.getMessage());
    }
    Mockito.verify(outputStream).close();
  }

출처 : http://apisonar.com/java-examples/org.mockito.Mockito.doThrow.html#Example-19

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.