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

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

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

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

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

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



답변

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

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

때로 간첩을 찌르기 위해 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();


답변

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

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(){}
}

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

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


답변

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


답변

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 대제사로부터 설명을 초대합니다 …

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


답변

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

@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));
}


답변

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

누군가는 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()일했습니다.


답변

파티에 늦었지만 위의 솔루션이 효과가 없었으므로 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());