Mockito : 메서드를 감시하려고하면 원래 메서드가 호출됩니다.


352

Mockito 1.9.0을 사용하고 있습니다. JUnit 테스트에서 클래스의 단일 메소드에 대한 동작을 모방하고 싶습니다.

final MyClass myClassSpy = Mockito.spy(myInstance);
Mockito.when(myClassSpy.method1()).thenReturn(myResults);

문제는 두 번째 줄에서 myClassSpy.method1()실제로 호출되어 예외가 발생한다는 것입니다. 내가 모의를 사용하는 유일한 이유는 나중에 myClassSpy.method1()호출 될 때마다 실제 메소드가 호출되지 않고 myResults객체가 반환되도록하기 때문입니다.

MyClass인터페이스이며 myInstance중요한 경우 구현입니다.

이 감시 행동을 바로 잡기 위해 어떻게해야합니까?


이것을보십시오 : stackoverflow.com/a/29394497/355438
Lu55

답변:


610

공식 문서를 인용하겠습니다 .

실제 물체를 감시하는 데 중요한 문제가 있습니다!

때로 간첩을 찌르기 위해 when (Object)를 사용하는 것이 불가능한 경우가 있습니다. 예:

List list = new LinkedList();
List spy = spy(list);

// Impossible: real method is called so spy.get(0) throws IndexOutOfBoundsException (the list is yet empty)
when(spy.get(0)).thenReturn("foo");

// You have to use doReturn() for stubbing
doReturn("foo").when(spy).get(0);

귀하의 경우에는 다음과 같습니다.

doReturn(resulstIWant).when(myClassSpy).method1();

27
이 방법을 사용하는데 원래 방법이 여전히 호출되면 어떻게합니까? 전달한 매개 변수에 문제가있을 수 있습니까? 다음은 전체 테스트는 다음과 같습니다 pastebin.com/ZieY790P의 send 메소드가 호출되고
예브게니 페트 로브

26
@EvgeniPetrov 원래 메소드가 여전히 호출되는 경우 원래 메소드가 최종이기 때문일 수 있습니다. Mockito는 최종 방법을 조롱하지 않으며 최종 방법의 조롱에 대해 경고 할 수 없습니다.
MarcG

1
doThrow ()에서도 가능합니까?
Gobliins

1
예, 불행히도 정적 메소드는 실행 불가능하고 "스파이 할 수 없습니다". 정적 메소드를 처리하기 위해 수행하는 작업은 정적 호출 주위에 메소드를 랩핑하고 해당 메소드에서 doNothing 또는 doReturn을 사용하는 것입니다. 싱글 톤이나 스칼라 객체를 사용하면 논리의 고기를 추상 클래스로 옮기고 스파이를 만들 수있는 객체에 대한 대체 테스트 클래스를 가질 수 있습니다.
Andrew Norman

24
NOT final 및 NOT static 메소드가 여전히 호출되면 어떻게 될까요?
X-HuMan

27

제 사례는 허용 된 답변과 다릅니다. 해당 패키지에 존재하지 않는 인스턴스에 대한 패키지 전용 메소드를 조롱하려고했습니다.

package common;

public class Animal {
  void packageProtected();
}

package instances;

class Dog extends Animal { }

그리고 시험 수업

package common;

public abstract class AnimalTest<T extends Animal> {
  @Before
  setup(){
    doNothing().when(getInstance()).packageProtected();
  }

  abstract T getInstance();
}

package instances;

class DogTest extends AnimalTest<Dog> {
  Dog getInstance(){
    return spy(new Dog());
  }

  @Test
  public void myTest(){}
}

컴파일은 정확하지만 테스트 설정을 시도하면 실제 메소드가 대신 호출됩니다.

보호 된 방법 또는 공개 방법을 선언하면 문제가 해결되지만 깨끗한 해결책은 아닙니다.


2
비슷한 문제가 발생했지만 테스트 및 패키지 전용 방법이 동일한 패키지에있었습니다. 아마도 Mockito는 일반적으로 패키지 전용 메소드에 문제가 있다고 생각합니다.
Dave

22

필자의 경우 Mockito 2.0을 사용 하여 실제 호출을 스텁하기 위해 모든 any()매개 변수를 변경해야했습니다 nullable().


2
321 투표 상위 답변 당신을 실망시키지 못하게, 이것은 내 문제를 해결 :) 나는 몇 시간 동안 이것으로 고투하고있다!
크리스 케셀

3
이것이 저에게 답이었습니다. 메소드를 조롱 할 때 따라 오는 사람들이 더 쉽게하기 위해 구문은 다음과 같습니다. foo = Mockito.spy(foo); Mockito.doReturn(someValue).when(foo).methodToPrevent(nullable(ArgumentType.class));
Stryder

Mockito 2.23.4와 나는 함께 잘 작동이 필요하지 않습니다 확인할 수 있습니다 anyeq정합 기.
vmaldosan

2
2.23.4 lib 버전에서 any (), eq () 및 nullable ()의 세 가지 접근 방식을 시도했습니다. 나중에 일한
ryzhman

안녕하세요, 귀하의 솔루션은 정말 좋고 나를 위해 일했습니다. 감사합니다
Dhiren Solanki

16

Tomasz Nurkiewicz의 대답은 전체 이야기를 말하지 않는 것처럼 보입니다!

NB Mockito 버전 : 1.10.19.

나는 매우 Mockito newb이므로 다음과 같은 행동을 설명 할 수 없습니다.이 답변을 향상시킬 수있는 전문가가 있다면 자유롭게 느끼십시오.

여기서 문제가되는 방법 getContentStringValueNOT finalNOT static 입니다.

이 줄 원래 메소드를 호출합니다 getContentStringValue.

doReturn( "dummy" ).when( im ).getContentStringValue( anyInt(), isA( ScoreDoc.class ));

이 줄 원래 메소드를 호출 하지 않습니다getContentStringValue .

doReturn( "dummy" ).when( im ).getContentStringValue( anyInt(), any( ScoreDoc.class ));

내가 대답 할 수없는 이유로을 사용 isA()하면 의도 된 (?) "방법을 호출하지 않음"동작이 doReturn실패합니다.

여기에 포함 된 메소드 시그니처를 살펴 보겠습니다 . 둘 다의 static메소드입니다 Matchers. 둘 다 Javadoc에 의해 리턴된다고 말하는데 null, 그 자체로 머리를 돌리기가 약간 어렵습니다. 아마도 Class매개 변수로 전달 된 객체는 검사되지만 결과는 계산되거나 버려지지 않습니다. 그 감안할 때 null모든 클래스에 대한 설 수 당신이의 서명을 호출하지 않도록 조롱 방법에 대한줬으면되지 않도록 isA( ... )하고 any( ... )바로 돌아 null일반적인 매개 변수보다는 * <T>?

어쨌든:

public static <T> T isA(java.lang.Class<T> clazz)

public static <T> T any(java.lang.Class<T> clazz)

API 설명서에는 이에 대한 힌트가 없습니다. 또한 이러한 "메소드를 호출하지 마십시오"동작에 대한 요구는 "매우 드물다"고합니다. 개인적으로 저는이 기술을 항상 사용합니다 . 일반적으로 조롱에는 몇 개의 라인이 "장면을 설정"하고 그 뒤에는 여러분이 준비한 모의 문맥에서 장면을 "재생"하는 메소드가 호출됩니다. 그리고 풍경과 소품을 설정하는 동안 배우가 마지막 단계에 들어가서 그들의 마음을 연기하기 시작하는 것입니다 ...

그러나 이것은 저의 급료보다 더 큽니다. 나는 지나가는 Mockito 대제사로부터 설명을 초대합니다 ...

* "일반 매개 변수"가 올바른 용어입니까?


이것이 명확성을 추가하거나 문제를 더 혼란스럽게하는지 여부는 모르겠지만 isA ()와 any ()의 차이점은 isA는 실제로 유형 검사를 수행하는 반면 any () 메서드 계열은 단순히 유형 캐스팅을 피하기 위해 만들어졌습니다. 논의.
케빈 웰커

@KevinWelker 감사합니다. 실제로 분석법 이름에는 특정한 설명이 필요하지 않습니다. 그러나 나는 적절하게 문서화하지 않은 천재적인 Mockito 디자이너들과 문제를 겪습니다. 의심 할 여지없이 Mockito에 관한 다른 책을 읽어야합니다. PS는 실제로 "중간 Mockito"를 가르 칠 자원이 거의없는 것 같습니다!
마이크 설치류

1
역사는 anyXX 메소드가 타입 캐스팅 만 처리하는 방법으로 처음 생성되었다는 것입니다. 그런 다음 인수 검사를 추가하라는 제안을 받았을 때 기존 API의 사용자를 중단하지 않기 위해 isA () 제품군을 만들었습니다. any () 메소드가 유형 검사를 모두 수행해야한다는 것을 알았으므로 Mockito 2.X 정밀 검사에서 다른 주요 변경 사항이 도입 될 때까지 변경을 지연했습니다 (아직 시도하지는 않았습니다). 2.x +에서 anyX () 메소드는 isA () 메소드의 별명입니다.
케빈 웰커

감사합니다. 실행에 사용 된 코드가 갑자기 조용히 실패하기 때문에 동시에 여러 라이브러리 업데이트를 수행하는 사람들에게는 이것이 중추적 인 답변입니다.
Dex Stakker

6

스파이와 관련하여 문제를 일으킬 수있는 또 하나의 시나리오는 스프링 빈 (스프링 테스트 프레임 워크 사용) 또는 테스트 중에 객체를 근접시키는 다른 프레임 워크를 테스트 할 때 입니다.

@Autowired
private MonitoringDocumentsRepository repository

void test(){
    repository = Mockito.spy(repository)
    Mockito.doReturn(docs1, docs2)
            .when(repository).findMonitoringDocuments(Mockito.nullable(MonitoringDocumentSearchRequest.class));
}

위의 코드에서 Spring과 Mockito는 MonitoringDocumentsRepository 객체를 프록시하려고 시도하지만 Spring이 먼저 시작되어 findMonitoringDocuments 메소드를 실제로 호출합니다. 리포지토리 객체에 스파이를 넣은 직후 코드를 디버깅하면 디버거 내부에서 다음과 같이 보입니다.

repository = MonitoringDocumentsRepository$$EnhancerBySpringCGLIB$$MockitoMock$

구조에 @SpyBean

@Autowired주석 대신 주석을 사용 @SpyBean하면 위의 문제를 해결할 것입니다 .SpyBean 주석은 리포지토리 객체도 주입하지만 Mockito가 먼저 프록시하고 디버거 내에서 다음과 같이 보입니다.

repository = MonitoringDocumentsRepository$$MockitoMock$$EnhancerBySpringCGLIB$

그리고 여기 코드가 있습니다 :

@SpyBean
private MonitoringDocumentsRepository repository

void test(){
    Mockito.doReturn(docs1, docs2)
            .when(repository).findMonitoringDocuments(Mockito.nullable(MonitoringDocumentSearchRequest.class));
}

1

스파이가 원래 메서드를 호출해야하는 또 다른 이유를 찾았습니다.

누군가는 final수업 을 조롱하려는 아이디어를 가지고 다음 에 대해 발견했습니다 MockMaker.

이 기능은 현재 메커니즘과 다르게 작동하며 다른 제한 사항이 있으며 경험과 사용자 피드백을 수집하려면이 기능을 명시 적으로 활성화해야 사용할 수 있습니다. src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker단일 행을 포함하는 파일을 작성하여 mockito 확장 메커니즘을 통해 수행 할 수 있습니다 .mock-maker-inline

출처 : https://github.com/mockito/mockito/wiki/What%27s-new-in-Mockito-2#mock-the-unmockable-opt-in-mocking-of-final-classesmethods

해당 파일을 병합하여 내 컴퓨터로 가져온 후 테스트에 실패했습니다.

방금 줄 (또는 파일)을 제거하고 spy()일했습니다.


이것이 내 경우의 이유 였고, 나는 최종 방법을 조롱하려고 시도했지만 혼란스러운 명확한 오류 메시지없이 실제 방법을 계속 호출했습니다.
Bashar Ali Labadi

1

파티에 늦었지만 위의 솔루션이 효과가 없었으므로 0.02 $를 공유했습니다.

Mokcito 버전 : 1.10.19

MyClass.java

private int handleAction(List<String> argList, String action)

테스트

MyClass spy = PowerMockito.spy(new MyClass());

다음은 나를 위해 작동하지 않았습니다 (실제 방법이 호출되었습니다).

1.

doReturn(0).when(spy , "handleAction", ListUtils.EMPTY_LIST, new String());

2.

doReturn(0).when(spy , "handleAction", any(), anyString());

삼.

doReturn(0).when(spy , "handleAction", null, null);

다음 작업 :

doReturn(0).when(spy , "handleAction", any(List.class), anyString());

0

클래스의 메소드가 호출되지 않도록하는 한 가지 방법은 더미로 메소드를 대체하는 것입니다.

    WebFormCreatorActivity activity = spy(new WebFormCreatorActivity(clientFactory) {//spy(new WebFormCreatorActivity(clientFactory));
            @Override
            public void select(TreeItem i) {
                log.debug("SELECT");
            };
        });

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