การใช้ Mockito กับการโทรหลายวิธีด้วยวิธีเดียวกันกับอาร์กิวเมนต์เดียวกัน


289

มีวิธีให้ stubbed วิธีการคืนค่าวัตถุต่าง ๆ ในการร้องขอต่อไป? ฉันต้องการทำสิ่งนี้เพื่อทดสอบคำตอบที่ไม่ExecutorCompletionServiceสิ้นสุดจาก คือการทดสอบโดยไม่คำนึงถึงลำดับการส่งคืนของวิธีการผลลัพธ์ยังคงที่

รหัสที่ฉันต้องการทดสอบมีลักษณะเช่นนี้

// Create an completion service so we can group these tasks together
ExecutorCompletionService<T> completionService =
        new ExecutorCompletionService<T>(service);

// Add all these tasks to the completion service
for (Callable<T> t : ts)
    completionService.submit(request);

// As an when each call finished, add it to the response set.
for (int i = 0; i < calls.size(); i ++) {
    try {
        T t = completionService.take().get();
        // do some stuff that I want to test
    } catch (...) { }        
}

คำตอบ:


254

คุณสามารถทำได้โดยใช้thenAnswerวิธีการ (เมื่อผูกพันกับwhen):

when(someMock.someMethod()).thenAnswer(new Answer() {
    private int count = 0;

    public Object answer(InvocationOnMock invocation) {
        if (count++ == 1)
            return 1;

        return 2;
    }
});

หรือใช้doAnswerวิธีคงที่เทียบเท่า:

doAnswer(new Answer() {
    private int count = 0;

    public Object answer(InvocationOnMock invocation) {
        if (count++ == 1)
            return 1;

        return 2;
    }
}).when(someMock).someMethod();

635

เกี่ยวกับ

when( method-call ).thenReturn( value1, value2, value3 );

คุณสามารถใส่อาร์กิวเมนต์ได้มากเท่าที่คุณต้องการในวงเล็บจากนั้นกลับมาหากพวกเขาเป็นประเภทที่ถูกต้องทั้งหมด ค่าแรกจะถูกส่งคืนในครั้งแรกที่มีการเรียกเมธอดจากนั้นคำตอบที่สองเป็นต้น ค่าสุดท้ายจะถูกส่งคืนซ้ำ ๆ เมื่อใช้ค่าอื่นทั้งหมดหมด


4
สิ่งนี้จะใช้ได้กับการเยาะเย้ย แต่ไม่ใช่กับสายลับ หากคุณต้องการป้องกันการโทรด้วยวิธีดั้งเดิมคุณต้องมี doAnswer (... ). เมื่อ (someSpy) .someMethod (... )
ยูริ

6
@Yuri - ไม่มาก คุณไม่จำเป็นต้องdoAnswerเขียนAnswerในกรณีที่คุณพูดถึง doReturn(...).when(someSpy).someMethod(...)คุณก็สามารถใช้ ดูเหมือนว่ามีเหตุผลที่จะคิดว่าเอ็มม่าสนใจใน mocks มากกว่าสายลับ แต่ฉันคิดว่าฉันสามารถเพิ่มบางอย่างในคำตอบเพื่อสะกดคำนี้ ขอบคุณสำหรับความคิดเห็น
Dawood ibn Kareem

@DawoodibnKareem ให้พูดสำหรับการโทรครั้งแรกที่ฉันต้องการคืนค่าและสำหรับการโทรครั้งที่สองฉันต้องการที่จะโยนข้อยกเว้น สิ่งนี้สามารถทำได้?
Rito

@Rito โปรดอ่านคำตอบของ Volodymyr หรือคำตอบของ Raystorm พวกเขาทั้งสองครอบคลุมกรณีนั้น
Dawood ibn Kareem

ช่างเป็นคำตอบที่น่ายินดี
wild_nothing

151

ดังที่ได้กล่าวไว้ก่อนหน้านี้ว่าการโทรเกือบทั้งหมดนั้นสามารถโยงได้

ดังนั้นคุณสามารถโทร

when(mock.method()).thenReturn(foo).thenReturn(bar).thenThrow(new Exception("test"));

//OR if you're mocking a void method and/or using spy instead of mock

doReturn(foo).doReturn(bar).doThrow(new Exception("Test").when(mock).method();

ข้อมูลอื่น ๆ ในMockito ของ Documenation


3
มีประโยชน์มาก! จะเกิดอะไรขึ้นในครั้งที่ 4 ที่mock.methodถูกเรียกในตัวอย่างนี้ ฉันต้องการสิ่งที่ชอบกลับฟูครั้งแรก แต่กลับบาร์สำหรับส่วนที่เหลือทั้งหมด
javaPlease42

6
การเรียกใช้เพิ่มเติมแต่ละครั้งบนจำลองจะส่งคืน 'thenReturn' หรือ 'thenThrow' ครั้งสุดท้ายที่มีประโยชน์มาก
Francois Lacoursiere

ขอบคุณสำหรับคำแนะนำที่ดีและเรียบง่าย ไม่เคยรู้เรื่องนี้มาก่อน ฉันพยายามหาวิธีคืนสองผลลัพธ์ที่ต่างกันในการโทรที่เหมือนกันสองครั้ง ช่วยฉันได้หลายครั้ง
CharlesC

68

คุณยังสามารถdoReturn()เรียกใช้เมธอดแบบลูกโซ่เช่นนี้ได้

doReturn(null).doReturn(anotherInstance).when(mock).method();

น่ารักใช่มั้ย :)


4

ฉันใช้MultipleAnswerห้องเรียนที่ช่วยให้ฉันสามารถตอบคำตอบที่แตกต่างกันในทุกการโทร นี่คือส่วนของรหัส:

private final class MultipleAnswer<T> implements Answer<T> {

    private final ArrayList<Answer<T>> mAnswers;

    MultipleAnswer(Answer<T>... answer) {
        mAnswers = new ArrayList<>();
        mAnswers.addAll(Arrays.asList(answer));
    }

    @Override
    public T answer(InvocationOnMock invocation) throws Throwable {
        return mAnswers.remove(0).answer(invocation);
    }
}

1
คุณสามารถเริ่มต้นวัตถุนั้นด้วยวิธีที่สั้นง่ายและอ่านได้หรือไม่?
usr-local-ΕΨΗΕΛΩΝ

1

การติดตามต่อไปนี้สามารถใช้เป็นวิธีการทั่วไปเพื่อส่งกลับอาร์กิวเมนต์ที่แตกต่างกันในการเรียกใช้เมธอดต่างๆ สิ่งเดียวที่เราต้องทำคือเราต้องผ่านอาร์เรย์ที่มีลำดับซึ่งวัตถุที่ควรดึงในการโทรแต่ละครั้ง

@SafeVarargs
public static <Mock> Answer<Mock> getAnswerForSubsequentCalls(final Mock... mockArr) {
    return new Answer<Mock>() {
       private int count=0, size=mockArr.length;
       public Mock answer(InvocationOnMock invocation) throws throwable {
           Mock mock = null;
           for(; count<size && mock==null; count++){
                mock = mockArr[count];
           }

           return mock;    
       } 
    }
}

อดีต getAnswerForSubsequentCalls(mock1, mock3, mock2);จะส่งคืนวัตถุ mock1 ในการโทรครั้งแรกวัตถุ mock3 ในการโทรครั้งที่สองและวัตถุ mock2 ในการโทรครั้งที่สาม ควรใช้แบบwhen(something()).doAnswer(getAnswerForSubsequentCalls(mock1, mock3, mock2)); นี้มันเกือบจะคล้ายกับwhen(something()).thenReturn(mock1, mock3, mock2);


1

เกี่ยวข้องกับคำตอบของ @ [Igor Nikolaev] เมื่อ 8 ปีที่แล้วการใช้Answerสามารถทำได้ง่ายกว่าโดยใช้นิพจน์แลมบ์ดาที่มีใน Java 8

when(someMock.someMethod()).thenAnswer(invocation -> {
    doStuff();
    return;
});

หรือมากกว่านั้น:

when(someMock.someMethod()).thenAnswer(invocation -> doStuff());

1

สไตล์ BDD:

import static org.mockito.BDDMockito.*;
...
    @Test
    void submit() {
        given(yourMock.yourMethod()).willReturn(1, 2, 3);

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