mockito를 사용하여 특정 유형의 목록을 캡처하는 방법


301

mockitos ArgumentCaptore를 사용하여 특정 유형의 목록을 캡처하는 방법이 있습니까? 작동하지 않습니다.

ArgumentCaptor<ArrayList<SomeType>> argument = ArgumentCaptor.forClass(ArrayList.class);

8
구체적인 목록 구현을 사용하는 것이 끔찍한 아이디어라는 것을 알았습니다 ( ArrayList). 항상 List인터페이스를 사용할 수 있으며 , 공변량이라는 사실을 나타내려면 다음을 사용할 수 있습니다 extends.ArgumentCaptor<? extends List<SomeType>>
tenshi

답변:


533

중첩 된 제네릭 문제는 @Captor 주석 으로 피할 수 있습니다 .

public class Test{

    @Mock
    private Service service;

    @Captor
    private ArgumentCaptor<ArrayList<SomeType>> captor;

    @Before
    public void init(){
        MockitoAnnotations.initMocks(this);
    }

    @Test 
    public void shouldDoStuffWithListValues() {
        //...
        verify(service).doStuff(captor.capture()));
    }
}

70
다른 러너를 사용할 수있는 기능을 배제하는 러너를 사용하는 대신 메소드 MockitoAnnotations.initMocks(this)에서 사용하는 것을 선호합니다 @Before. 그러나 +1은 주석을 지적 해 주셔서 감사합니다.
John B

4
이 예제가 완료되었는지 확실하지 않습니다. 내가 얻을 ... 오류 : (240, 40) 자바 : 변수 체포 텐시의 대답은 아래처럼 초기화되지 않았
마이클 Dausmann

1
나는 같은 문제에 부딪 쳤고 blog.jdriven.com/2012/10/… . 클래스에 주석을 넣은 후 MockitoAnnotations.initMocks를 사용하는 단계가 포함되어 있습니다. 내가 알았던 한 가지는 로컬 변수 내에 가질 수 없다는 것입니다.
SlopeOak

1
@ chamzz.dot ArgumentCaptor <ArrayList <SomeType >> captor; 이미 "SomeType"배열을 캡처하고 있습니다. <-특정 유형입니까?
Miguel R. Santaella

1
나는 보통 Captor 선언에서 ArrayList 대신 List를 선호한다 : ArgumentCaptor <List <SomeType >> captor;
Miguel R. Santaella

146

예, 이것은 일반적인 특정 문제가 아니라 모의 특유의 문제입니다.

에 대한 클래스 객체가 없으므로 ArrayList<SomeType>해당 객체를을 (를) 요구하는 메서드에 형식적으로 전달할 수 없습니다 Class<ArrayList<SomeType>>.

객체를 올바른 유형으로 캐스트 할 수 있습니다.

Class<ArrayList<SomeType>> listClass =
              (Class<ArrayList<SomeType>>)(Class)ArrayList.class;
ArgumentCaptor<ArrayList<SomeType>> argument = ArgumentCaptor.forClass(listClass);

이것은 안전하지 않은 캐스트에 대한 몇 가지 경고를 줄 것이다, 그리고 물론 당신의 ArgumentCaptor 정말 구별 할 수 ArrayList<SomeType>ArrayList<AnotherType>어쩌면 요소를 검사하지 않고.

(이것은 일반적인 제네릭 문제가 동안으로는와 유형 안전 문제에 대한 Mockito 특정 솔루션이, 다른 대답에 언급 @Captor주석이. 그것은 여전히 구별 할 수 없습니다 ArrayList<SomeType>ArrayList<OtherType>.)

편집하다:

tenshi 의 의견 도 살펴보십시오 . 당신은에서 원래의 코드를 변경할 수 있습니다 파울로 Ebermann 이 (훨씬 더 간단)로

final ArgumentCaptor<List<SomeType>> listCaptor
        = ArgumentCaptor.forClass((Class) List.class);

49
자바가 정적 메소드 호출에 대한 타입 추론을한다는 사실에 근거하여 보여준 예제를 단순화 할 수있다 :ArgumentCaptor<List<SimeType>> argument = ArgumentCaptor.forClass((Class) List.class);
tenshi

4
검사되지 않거나 안전하지 않은 작업 사용 경고 를 비활성화하려면 @SuppressWarnings("unchecked")인수 캡처 기 정의 줄 위의 주석을 사용하십시오 . 또한 캐스팅 Class은 중복됩니다.
mrts

1
Class테스트에서 캐스팅 이 중복되지 않습니다.
Wim Deblauwe

16

오래된 자바 스타일 (비 유형의 안전한 제네릭) 의미를 두려워하지 않는 경우에도 작동하며 상당히 간단합니다.

ArgumentCaptor<List> argument = ArgumentCaptor.forClass(List.class);
verify(subject.method(argument.capture()); // run your code
List<SomeType> list = argument.getValue(); // first captured List, etc.

2
선언하기 전에 @SuppressWarnings ( "rawtypes")를 추가하여 경고를 비활성화 할 수 있습니다.
pkalinow

9
List<String> mockedList = mock(List.class);

List<String> l = new ArrayList();
l.add("someElement");

mockedList.addAll(l);

ArgumentCaptor<List> argumentCaptor = ArgumentCaptor.forClass(List.class);

verify(mockedList).addAll(argumentCaptor.capture());

List<String> capturedArgument = argumentCaptor.<List<String>>getValue();

assertThat(capturedArgument, hasItem("someElement"));

4

@tenshi와 @pkalinow의 의견 (@rogerdpack에 대한 의견)을 바탕으로 다음은 "확인되지 않거나 안전하지 않은 작업 사용" 경고를 비활성화하는 목록 인수 캡처자를 만드는 간단한 솔루션입니다 .

@SuppressWarnings("unchecked")
final ArgumentCaptor<List<SomeType>> someTypeListArgumentCaptor =
    ArgumentCaptor.forClass(List.class);

여기에 전체 예제 와 해당하는 전달 CI 빌드 및 테스트 실행이 있습니다 .

우리 팀은 단위 테스트에서 한동안 이것을 사용해 왔으며 이것은 우리에게 가장 간단한 해결책처럼 보입니다.


2

이전 버전의 junit의 경우 다음을 수행 할 수 있습니다.

Class<Map<String, String>> mapClass = (Class) Map.class;
ArgumentCaptor<Map<String, String>> mapCaptor = ArgumentCaptor.forClass(mapClass);

1

내 Android 앱에서 테스트 활동과 동일한 문제가 발생했습니다. 나는 사용 ActivityInstrumentationTestCase2하고 MockitoAnnotations.initMocks(this);작동하지 않았다. 각 필드가있는 다른 클래스 로이 문제를 해결했습니다. 예를 들면 다음과 같습니다.

class CaptorHolder {

        @Captor
        ArgumentCaptor<Callback<AuthResponse>> captor;

        public CaptorHolder() {
            MockitoAnnotations.initMocks(this);
        }
    }

그런 다음 활동 테스트 방법에서 :

HubstaffService hubstaffService = mock(HubstaffService.class);
fragment.setHubstaffService(hubstaffService);

CaptorHolder captorHolder = new CaptorHolder();
ArgumentCaptor<Callback<AuthResponse>> captor = captorHolder.captor;

onView(withId(R.id.signInBtn))
        .perform(click());

verify(hubstaffService).authorize(anyString(), anyString(), captor.capture());
Callback<AuthResponse> callback = captor.getValue();

0

Mockito의 GitHub의에서 공개 문제 이 정확한 문제에 대한이.

테스트에서 주석을 사용하지 않는 간단한 해결 방법을 찾았습니다.

import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.MockitoAnnotations;

public final class MockitoCaptorExtensions {

    public static <T> ArgumentCaptor<T> captorFor(final CaptorTypeReference<T> argumentTypeReference) {
        return new CaptorContainer<T>().captor;
    }

    public static <T> ArgumentCaptor<T> captorFor(final Class<T> argumentClass) {
        return ArgumentCaptor.forClass(argumentClass);
    }

    public interface CaptorTypeReference<T> {

        static <T> CaptorTypeReference<T> genericType() {
            return new CaptorTypeReference<T>() {
            };
        }

        default T nullOfGenericType() {
            return null;
        }

    }

    private static final class CaptorContainer<T> {

        @Captor
        private ArgumentCaptor<T> captor;

        private CaptorContainer() {
            MockitoAnnotations.initMocks(this);
        }

    }

}

여기에서 일어나는 일은 우리가 새로운 클래스 만들 것입니다 @Captor 주석을 그것으로 체포를 주입. 그런 다음 캡터를 추출하여 정적 메서드에서 반환합니다.

테스트에서 다음과 같이 사용할 수 있습니다.

ArgumentCaptor<Supplier<Set<List<Object>>>> fancyCaptor = captorFor(genericType());

또는 Jackson의 것과 유사한 구문으로 TypeReference:

ArgumentCaptor<Supplier<Set<List<Object>>>> fancyCaptor = captorFor(
    new CaptorTypeReference<Supplier<Set<List<Object>>>>() {
    }
);

Mockito는 실제로 직렬 변환기와 달리 유형 정보가 필요하지 않기 때문에 작동합니다.

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