Mockito: วิธีการทิ่มแทงที่ส่งคืนประเภทด้วย Wild-Cards ที่ถูกผูกไว้


140

พิจารณารหัสนี้:

public class DummyClass {
    public List<? extends Number> dummyMethod() {
        return new ArrayList<Integer>();
    }
}
public class DummyClassTest {
    public void testMockitoWithGenerics() {
        DummyClass dummyClass = Mockito.mock(DummyClass.class);
        List<? extends Number> someList = new ArrayList<Integer>();
        Mockito.when(dummyClass.dummyMethod()).thenReturn(someList); //Compiler complains about this
    }
}

คอมไพเลอร์บ่นเกี่ยวกับบรรทัดที่พยายามทำให้พฤติกรรมสำหรับdummyMethod(). คำแนะนำใด ๆ เกี่ยวกับวิธีการที่ใช้วิธีการขีดฆ่าที่ส่งคืนประเภทที่มีไวด์การ์ดที่มีขอบเขต?


คุณสามารถอัปเดตข้อมูลโค้ดของคุณเพื่อแสดงประเภททั่วไปได้หรือไม่
Millhouse

1
เสร็จแล้ว ฉันต้องลบแท็กก่อนและโค้ดออกพวกเขากำลังลอกออก <? ขยาย Number> จากการประกาศประเภท
Shikhar Mishra

คำตอบ:


196

คุณยังสามารถใช้วิธีdoReturn ที่ไม่ใช่ประเภทปลอดภัยเพื่อจุดประสงค์นี้

@Test
public void testMockitoWithGenerics()
{
    DummyClass dummyClass = Mockito.mock(DummyClass.class);
    List<? extends Number> someList = new ArrayList<Integer>();

    Mockito.doReturn(someList).when(dummyClass).dummyMethod();

    Assert.assertEquals(someList, dummyClass.dummyMethod());
}

ตามที่กล่าวไว้ในกลุ่ม Google ของ Mockito

แม้ว่าวิธีนี้จะง่ายกว่าthenAnswerแต่โปรดทราบอีกครั้งว่าประเภทนี้ไม่ปลอดภัย หากคุณกังวลเกี่ยวกับความปลอดภัยของประเภทคำตอบของ Millhouse นั้นถูกต้อง

รายละเอียดเพิ่มเติม

เพื่อความชัดเจนนี่คือข้อผิดพลาดของคอมไพเลอร์ที่สังเกตได้

The method thenReturn(List<capture#1-of ? extends Number>) in the type OngoingStubbing<List<capture#1-of ? extends Number>> is not applicable for the arguments (List<capture#2-of ? extends Number>)

ฉันเชื่อว่าคอมไพเลอร์ได้กำหนดประเภทตัวแทนแรกในระหว่างการwhenโทรและจากนั้นไม่สามารถยืนยันได้ว่าประเภทตัวแทนที่สองในการthenReturnโทรนั้นเหมือนกัน

ดูเหมือนว่าthenAnswerจะไม่พบปัญหานี้เนื่องจากยอมรับประเภทสัญลักษณ์แทนในขณะที่thenReturnใช้ประเภทที่ไม่ใช่สัญลักษณ์แทนซึ่งจะต้องถูกจับ จาก Mockito's OngoingStubbing ,

OngoingStubbing<T> thenAnswer(Answer<?> answer);
OngoingStubbing<T> thenReturn(T value);

สิ่งนี้ช่วยฉันได้บางส่วนเช่นกัน ... แต่จะเกิดอะไรขึ้นถ้ารายการที่คุณคาดว่าจะกลับมาไม่ว่างเปล่า?
ttati

แทนที่จะมีรายการว่างคุณสามารถทำได้: List <Number> someList = new ArrayList <Integer> (); someList.add (aNumber);
ttati

32

ฉันสมมติว่าคุณต้องการที่จะสามารถโหลดsomeListค่าที่ทราบได้ นี่คือวิธีการที่ใช้Answer<T>ร่วมกับวิธีการช่วยเหลือแม่แบบเพื่อให้ทุกอย่างปลอดภัย:

@Test
public void testMockitoWithGenericsUsingAnswer()
{
    DummyClass dummyClass =  Mockito.mock(DummyClass.class);

    Answer<List<Integer>> answer = setupDummyListAnswer(77, 88, 99);
    Mockito.when(dummyClass.dummyMethod()).thenAnswer(answer);

    ...
}

private <N extends Number> Answer<List<N>> setupDummyListAnswer(N... values) {
    final List<N> someList = new ArrayList<N>();

    someList.addAll(Arrays.asList(values));

    Answer<List<N>> answer = new Answer<List<N>>() {
        public List<N> answer(InvocationOnMock invocation) throws Throwable {
            return someList;
        }   
    };
    return answer;
}

18

ฉันตีสิ่งเดียวกันเมื่อวานนี้ ทั้งสองคำตอบจาก @ nondescript1 และ @millhouse ช่วยให้ฉันหาวิธีแก้ปัญหาได้ ฉันเคยใช้รหัสเดียวกันกับ @millhouse มากยกเว้นว่าฉันทำให้มันกว้างขึ้นเล็กน้อยเพราะข้อผิดพลาดของฉันไม่ได้เกิดจาก a java.util.Listแต่เป็นไฟล์com.google.common.base.Optional. วิธีผู้ช่วยตัวน้อยของฉันจึงอนุญาตให้ใช้ทุกประเภทTไม่ใช่แค่List<T>:

public static <T> Answer<T> createAnswer(final T value) {
    Answer<T> dummy = new Answer<T>() {
        @Override
        public T answer(InvocationOnMock invocation) throws Throwable {
            return value;
        }
    };
    return dummy;
}

ด้วยวิธีการช่วยเหลือนี้คุณสามารถเขียน:

Mockito.when(dummyClass.dummyMethod()).thenAnswer(createAnswer(someList));

สิ่งนี้รวบรวมได้ดีและทำสิ่งเดียวกับthenReturn(...)วิธีการ

มีใครรู้บ้างว่าข้อผิดพลาดที่คอมไพลเลอร์ Java ปล่อยออกมานั้นเป็นบั๊กของคอมไพเลอร์หรือโค้ดไม่ถูกต้องจริงๆ?


สิ่งนี้ดูเหมือนตรงไปตรงมาเรียบง่ายและใกล้เคียงที่สุดที่ฉันสามารถบอกได้ถูกต้อง ฉันไม่แน่ใจว่าทำไม Mockito ไม่ให้สิ่งที่คล้ายกันนี้ ....... เว้นแต่จะเป็นเช่นนั้น?
ว่าง

17
ใน Java 8 สามารถย่อ: Mockito.when(dummyClass.dummyMethod()).thenAnswer(x -> someList)ดังนั้นไม่จำเป็นต้องใช้วิธียูทิลิตี้
fikovnik

1
@fikovnik ช่างเป็นการค้นพบที่ยอดเยี่ยม "thenAnswer"!
borjab

6

ฉันเปลี่ยนความคิดเห็นของfikovnikให้เป็นคำตอบที่นี่เพื่อให้มีการมองเห็นมากขึ้นเนื่องจากฉันคิดว่าเป็นโซลูชันที่หรูหราที่สุดโดยใช้ Java 8+

เอกสาร Mockitoแนะนำให้ใช้doReturn()(ตามข้อเสนอแนะในคำตอบที่ได้รับการยอมรับ) เท่านั้นเป็นที่พึ่งสุดท้าย

เพื่อหลีกเลี่ยงข้อผิดพลาดของคอมไพเลอร์ที่อธิบายไว้ในคำถามคุณwhen()สามารถใช้วิธีMockito ที่แนะนำthenAnswer()และแลมด้า(แทนวิธีการช่วยเหลือ):

Mockito.when(mockedClass.mockedMethod()).thenAnswer(x -> resultList)

แม้ว่าจะไม่มีข้อผิดพลาดเวลาคอมไพล์ แต่รายการที่ส่งคืนจะว่างเปล่าแม้ว่าเราจะส่งผ่านรายการที่มีรายการก็ตาม
Venkatesh Kolla - ผู้ใช้ 2742897

0

แม้ว่าวิธีการยูทิลิตี้ที่เสนอโดย Marek Radonsky จะใช้งานได้ แต่ก็มีตัวเลือกอื่น ๆ ที่ไม่จำเป็นต้องใช้นิพจน์แลมบ์ดา (IMHO แปลก ๆ ) ที่ fikovnik แนะนำ:

เมื่อคำตอบของคำถามที่คล้ายกันนี้แสดงให้เห็นคุณสามารถใช้สิ่งต่อไปนี้:

BDDMockito.willReturn(someList).given(dummyClass).dummyMethod();
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.