มีวิธีในการจับภาพรายการประเภทเฉพาะโดยใช้ mockitos ArgumentCaptore สิ่งนี้ใช้ไม่ได้:
ArgumentCaptor<ArrayList<SomeType>> argument = ArgumentCaptor.forClass(ArrayList.class);
มีวิธีในการจับภาพรายการประเภทเฉพาะโดยใช้ mockitos ArgumentCaptore สิ่งนี้ใช้ไม่ได้:
ArgumentCaptor<ArrayList<SomeType>> argument = ArgumentCaptor.forClass(ArrayList.class);
คำตอบ:
ข้อมูลทั่วไปเกี่ยวกับปัญหาซ้อนสามารถหลีกเลี่ยงได้ด้วยคำอธิบายประกอบ @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)
ใน@Before
วิธีการแทนที่จะใช้นักวิ่งที่ไม่รวมความสามารถในการใช้นักวิ่งคนอื่น อย่างไรก็ตาม +1 ขอบคุณสำหรับการชี้ให้เห็นคำอธิบายประกอบ
ใช่นี่เป็นปัญหาทั่วไปเรื่องทั่วไปไม่ใช่เฉพาะม็อกคิโตะ
ไม่มีวัตถุชั้นเรียนสำหรับเป็นและทำให้คุณไม่สามารถพิมพ์-อย่างปลอดภัยผ่านวัตถุดังกล่าวเป็นวิธีการที่ต้องใช้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 คุณสามารถเปลี่ยนรหัสต้นฉบับจากPaŭlo Ebermannเป็นแบบนี้ (ง่ายกว่ามาก)
final ArgumentCaptor<List<SomeType>> listCaptor
= ArgumentCaptor.forClass((Class) List.class);
ArgumentCaptor<List<SimeType>> argument = ArgumentCaptor.forClass((Class) List.class);
@SuppressWarnings("unchecked")
คำอธิบายประกอบด้านบนบรรทัดคำจำกัดความตัวระบุอาร์กิวเมนต์ นอกจากนี้การหล่อเพื่อทำClass
ซ้ำซ้อน
Class
ไม่ได้ซ้ำซ้อนในการทดสอบของฉัน
หากคุณไม่กลัวความหมายทั่วไปของ java-style (ไม่ใช่ประเภททั่วไปที่ปลอดภัย) สิ่งนี้ก็ใช้งานได้และค่อนข้างง่ายเช่นกัน:
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"));
จากความเห็นของ @ tenhi และ @ pkalinow (เช่นความรุ่งโรจน์ถึง @rogerdpack) ต่อไปนี้เป็นวิธีแก้ไขปัญหาง่ายๆสำหรับการสร้างรายการตัวดักจับอาร์กิวเมนต์ที่ปิดใช้งานคำเตือน"ใช้การดำเนินการที่ไม่ จำกัด หรือไม่ปลอดภัย" :
@SuppressWarnings("unchecked")
final ArgumentCaptor<List<SomeType>> someTypeListArgumentCaptor =
ArgumentCaptor.forClass(List.class);
ตัวอย่างเต็มรูปแบบที่นี่และสอดคล้องกันผ่านการสร้าง CI และทดสอบการทำงานที่นี่
ทีมของเราใช้สิ่งนี้มาเป็นระยะเวลาหนึ่งในการทดสอบหน่วยของเราและนี่เป็นวิธีที่ตรงไปตรงมาที่สุดสำหรับเรา
สำหรับ Junit รุ่นก่อนหน้าคุณสามารถทำได้
Class<Map<String, String>> mapClass = (Class) Map.class;
ArgumentCaptor<Map<String, String>> mapCaptor = ArgumentCaptor.forClass(mapClass);
ฉันมีปัญหาเดียวกันกับกิจกรรมการทดสอบในแอพ 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();
มีปัญหาแบบเปิดใน GitHub ของ Mockitoเกี่ยวกับปัญหาที่แน่นอนนี้
ฉันพบวิธีแก้ไขง่ายๆที่ไม่ได้บังคับให้คุณใช้คำอธิบายประกอบในการทดสอบของคุณ:
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());
หรือด้วยไวยากรณ์ที่คล้ายกับแจ็คสันTypeReference
:
ArgumentCaptor<Supplier<Set<List<Object>>>> fancyCaptor = captorFor(
new CaptorTypeReference<Supplier<Set<List<Object>>>>() {
}
);
ใช้งานได้เพราะ Mockito ไม่ต้องการข้อมูลประเภทใด ๆ (เช่น serializers)
ArrayList
) คุณสามารถใช้List
ส่วนต่อประสานได้ตลอดเวลาและหากคุณต้องการให้เป็นตัวแทนของความจริงมันเป็น covariant คุณสามารถใช้extends
:ArgumentCaptor<? extends List<SomeType>>