Có cách nào để nắm bắt một danh sách các loại cụ thể bằng mockitos ArgumentCaptore. Điều này không hoạt động:
ArgumentCaptor<ArrayList<SomeType>> argument = ArgumentCaptor.forClass(ArrayList.class);
Có cách nào để nắm bắt một danh sách các loại cụ thể bằng mockitos ArgumentCaptore. Điều này không hoạt động:
ArgumentCaptor<ArrayList<SomeType>> argument = ArgumentCaptor.forClass(ArrayList.class);
Câu trả lời:
Vấn đề chung chung lồng nhau có thể tránh được với chú thích @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()));
}
}
MockitoAnnotations.initMocks(this)
trong @Before
phương thức hơn là sử dụng một người chạy loại trừ khả năng sử dụng một người chạy khác. Tuy nhiên, +1, cảm ơn vì đã chỉ ra chú thích.
Vâng, đây là một vấn đề chung chung, không phải là mockito cụ thể.
Không có đối tượng lớp nào ArrayList<SomeType>
, và do đó bạn không thể nhập một cách an toàn một đối tượng như vậy vào một phương thức yêu cầu a Class<ArrayList<SomeType>>
.
Bạn có thể chuyển đối tượng sang đúng loại:
Class<ArrayList<SomeType>> listClass =
(Class<ArrayList<SomeType>>)(Class)ArrayList.class;
ArgumentCaptor<ArrayList<SomeType>> argument = ArgumentCaptor.forClass(listClass);
Điều này sẽ đưa ra một số cảnh báo về các phôi không an toàn và tất nhiên ArgumentCaptor của bạn không thể thực sự phân biệt giữa ArrayList<SomeType>
và ArrayList<AnotherType>
không có thể kiểm tra các yếu tố.
(Như đã đề cập trong câu trả lời khác, trong khi đây là một vấn đề chung chung, có một giải pháp dành riêng cho Mockito cho vấn đề an toàn loại với @Captor
chú thích. Nó vẫn không thể phân biệt giữa một ArrayList<SomeType>
và một ArrayList<OtherType>
.)
Hãy cũng xem một bình luận của chục . Bạn có thể thay đổi mã gốc từ Paŭlo Ebermann sang mã này (đơn giản hơn nhiều)
final ArgumentCaptor<List<SomeType>> listCaptor
= ArgumentCaptor.forClass((Class) List.class);
ArgumentCaptor<List<SimeType>> argument = ArgumentCaptor.forClass((Class) List.class);
@SuppressWarnings("unchecked")
chú thích phía trên dòng định nghĩa trình giới hạn đối số. Ngoài ra, đúc đến Class
là dư thừa.
Class
không thừa trong các thử nghiệm của tôi.
Nếu bạn không sợ ngữ nghĩa cũ (không phải loại an toàn chung) kiểu java, thì điều này cũng hoạt động và khá đơn giản:
ArgumentCaptor<List> argument = ArgumentCaptor.forClass(List.class);
verify(subject.method(argument.capture()); // run your code
List<SomeType> list = argument.getValue(); // first captured List, etc.
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"));
Dựa trên các nhận xét của @ chụchihi và @ pkalinow (cũng là kudos cho @rogerdpack), sau đây là một giải pháp đơn giản để tạo một trình tạo đối số danh sách cũng vô hiệu hóa cảnh báo "sử dụng các hoạt động không được kiểm soát hoặc không an toàn" :
@SuppressWarnings("unchecked")
final ArgumentCaptor<List<SomeType>> someTypeListArgumentCaptor =
ArgumentCaptor.forClass(List.class);
Ví dụ đầy đủ ở đây và xây dựng CI tương ứng và chạy thử ở đây .
Nhóm của chúng tôi đã sử dụng điều này một thời gian trong các thử nghiệm đơn vị của chúng tôi và đây có vẻ là giải pháp đơn giản nhất cho chúng tôi.
Đối với phiên bản cũ hơn của Junit, bạn có thể làm
Class<Map<String, String>> mapClass = (Class) Map.class;
ArgumentCaptor<Map<String, String>> mapCaptor = ArgumentCaptor.forClass(mapClass);
Tôi gặp vấn đề tương tự với hoạt động thử nghiệm trong ứng dụng Android của mình. Tôi đã sử dụng ActivityInstrumentationTestCase2
và MockitoAnnotations.initMocks(this);
không làm việc. Tôi đã giải quyết vấn đề này với một lớp khác với trường tương ứng. Ví dụ:
class CaptorHolder {
@Captor
ArgumentCaptor<Callback<AuthResponse>> captor;
public CaptorHolder() {
MockitoAnnotations.initMocks(this);
}
}
Sau đó, trong phương pháp kiểm tra hoạt động:
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();
Có một vấn đề mở trong GitHub của Mockito về vấn đề chính xác này.
Tôi đã tìm thấy một cách giải quyết đơn giản không bắt buộc bạn phải sử dụng các chú thích trong các bài kiểm tra của mình:
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);
}
}
}
Chuyện gì xảy ra ở đây là chúng ta tạo ra một lớp mới với các @Captor
chú thích và tiêm Captor vào nó. Sau đó, chúng tôi chỉ trích xuất captor và trả về từ phương thức tĩnh của chúng tôi.
Trong thử nghiệm của bạn, bạn có thể sử dụng nó như vậy:
ArgumentCaptor<Supplier<Set<List<Object>>>> fancyCaptor = captorFor(genericType());
Hoặc với cú pháp giống với Jackson TypeReference
:
ArgumentCaptor<Supplier<Set<List<Object>>>> fancyCaptor = captorFor(
new CaptorTypeReference<Supplier<Set<List<Object>>>>() {
}
);
Nó hoạt động, bởi vì Mockito thực sự không cần bất kỳ loại thông tin nào (chẳng hạn như serial serial chẳng hạn).
ArrayList
). Bạn luôn có thể sử dụngList
giao diện và nếu bạn muốn đại diện cho sự thật, đó là sự đồng biến, thì bạn có thể sử dụngextends
:ArgumentCaptor<? extends List<SomeType>>