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


202

ดังนั้นฉันกำลังสร้างวัตถุจำลองเป็นตัวแปรแบบคงที่ในระดับชั้นเช่นนี้ ... ในการทดสอบหนึ่งฉันต้องการFoo.someMethod()คืนค่าบางค่าในขณะที่การทดสอบอื่นฉันต้องการให้คืนค่าที่แตกต่างกัน ปัญหาที่ฉันมีคือดูเหมือนว่าฉันต้องสร้าง mocks ขึ้นใหม่เพื่อให้ทำงานได้อย่างถูกต้อง ฉันต้องการหลีกเลี่ยงการสร้าง mocks ขึ้นใหม่และใช้วัตถุเดียวกันในการทดสอบแต่ละครั้ง

class TestClass {

    private static Foo mockFoo;

    @BeforeClass
    public static void setUp() {
        mockFoo = mock(Foo.class);
    }

    @Test
    public void test1() {
        when(mockFoo.someMethod()).thenReturn(0);

        TestObject testObj = new TestObject(mockFoo);

        testObj.bar(); // calls mockFoo.someMethod(), receiving 0 as the value

    }

    @Test
    public void test2() {
        when(mockFoo.someMethod()).thenReturn(1);

        TestObject testObj = new TestObject(mockFoo);

        testObj.bar(); // calls mockFoo.someMethod(), STILL receiving 0 as the value, instead of expected 1.

    }

}

ในการทดสอบครั้งที่สองฉันยังคงได้รับ 0 เป็นค่าเมื่อเรียกว่า testObj.bar () ... วิธีที่ดีที่สุดในการแก้ไขปัญหานี้คืออะไร โปรดทราบว่าฉันรู้ว่าฉันสามารถใช้การจำลองที่แตกต่างกันFooในการทดสอบแต่ละครั้งอย่างไรก็ตามฉันต้องmockFooเชื่อมโยงคำขอจำนวนมากออกไปซึ่งหมายความว่าฉันต้องทำการผูกมัดในการทดสอบแต่ละครั้ง

คำตอบ:


43

ประการแรกไม่ได้ทำให้จำลองคงที่ ทำให้เป็นเขตข้อมูลส่วนตัว เพียงแค่ใส่ชั้นตั้งค่าของคุณในไม่ได้@Before @BeforeClassอาจเป็นพวง แต่ราคาถูก

ประการที่สองวิธีที่คุณมีในขณะนี้คือวิธีที่ถูกต้องในการจำลองเพื่อคืนสิ่งต่าง ๆ โดยขึ้นอยู่กับการทดสอบ


438

คุณสามารถStub การโทรติดต่อกัน (# 10 in 2.8.9 api) ในกรณีนี้คุณจะใช้การเรียกแบบส่งคืนแล้วหลายครั้งหรือหนึ่งการโทรกลับแล้วหนึ่งครั้งพร้อมกับหลายพารามิเตอร์ (varargs)

import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import org.junit.Before;
import org.junit.Test;

public class TestClass {

    private Foo mockFoo;

    @Before
    public void setup() {
        setupFoo();
    }

    @Test
    public void testFoo() {
        TestObject testObj = new TestObject(mockFoo);

        assertEquals(0, testObj.bar());
        assertEquals(1, testObj.bar());
        assertEquals(-1, testObj.bar());
        assertEquals(-1, testObj.bar());
    }

    private void setupFoo() {
        mockFoo = mock(Foo.class);

        when(mockFoo.someMethod())
            .thenReturn(0)
            .thenReturn(1)
            .thenReturn(-1); //any subsequent call will return -1

        // Or a bit shorter with varargs:
        when(mockFoo.someMethod())
            .thenReturn(0, 1, -1); //any subsequent call will return -1
    }
}

171
ฉันคิดว่าคุณสามารถใช้ประโยชน์จากความจริงที่ว่า. thenReturn () รับค่า varargs ดังนั้นโค้ดสามารถย่อให้เหลือน้อยลงเมื่อ: (mockFoo.someMethod ()) .Return (0, 1, -1);
Justin Muller

10
@JustinMuller - ที่คุ้มค่าของคำตอบที่แยกจากกันผมคิดว่า (เมื่อเทียบกับความคิดเห็น)
ไบรอัน Agnew

16
คำตอบนี้ไม่ใช่สิ่งที่ถูกต้องที่จะทำในกรณีนี้ หากคุณต้นขั้ววิธีนี้จะกลับ 0 และ 1 แล้วคุณจะปรับตราบใดที่คุณทำงานแล้วtest1 test2แต่อาจเป็นไปได้ว่าสภาพแวดล้อมการรวมต่อเนื่องของคุณจะทำการทดสอบตามลำดับอื่น ๆ หรืออาจเป็นได้ว่าคุณต้องการให้ทำงานtest2ด้วยตัวเองโดยไม่เรียกใช้test1ก่อนซึ่งในกรณีนี้จะล้มเหลว การทดสอบหน่วยต้องเป็นอิสระจากกัน และไม่ควรมีการพึ่งพาระหว่างการทดสอบเดี่ยวหรือการพึ่งพาในการเรียงลำดับการทดสอบโดยเฉพาะ ในขณะที่thenReturnคำสั่งผูกมัด...
Dawood ibn Kareem

4
... มีการใช้งานเช่นเดียวกับการใช้ varargs สำหรับเดี่ยวthenReturnมันไม่ได้เป็นทางออกที่ถูกต้องในกรณีนี้ สำหรับฉันดูเหมือนว่าพยุหะของผู้ต่อต้านที่นี่มักจะล้มเหลวในการเข้าใจคำถาม
Dawood ibn Kareem

2
Junit เองไม่มั่นใจในคำสั่งทดสอบหากไม่มี@FixMethodOrder
Roger

29

สำหรับทุกคนที่ค้นหาเพื่อส่งคืนบางสิ่งจากนั้นจึงมีข้อยกเว้นการโทรอีกครั้ง:

    when(mockFoo.someMethod())
            .thenReturn(obj1)
            .thenReturn(obj2)
            .thenThrow(new RuntimeException("Fail"));

หรือ

    when(mockFoo.someMethod())
            .thenReturn(obj1, obj2)
            .thenThrow(new RuntimeException("Fail"));


14

สำหรับทุกคนที่ใช้สอดแนม ()และ doReturn () แทนเมธอด when ():

สิ่งที่คุณต้องการส่งคืนวัตถุที่แตกต่างกันในการโทรที่แตกต่างกันคือ:

doReturn(obj1).doReturn(obj2).when(this.spyFoo).someMethod();

.

สำหรับ mocks คลาสสิก:

when(this.mockFoo.someMethod()).thenReturn(obj1, obj2);

หรือมีข้อยกเว้นถูกโยน:

when(mockFoo.someMethod())
        .thenReturn(obj1)
        .thenThrow(new IllegalArgumentException())
        .thenReturn(obj2, obj3);
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.