ตรวจพบ Stubbing ที่ยังไม่เสร็จใน Mockito


151

ฉันได้รับข้อยกเว้นต่อไปนี้ขณะทำการทดสอบ ฉันใช้ Mockito เพื่อการเยาะเย้ย คำแนะนำที่กล่าวถึงโดยห้องสมุด Mockito ไม่ได้ช่วยอะไร

org.mockito.exceptions.misusing.UnfinishedStubbingException: 
Unfinished stubbing detected here:
    -> at com.a.b.DomainTestFactory.myTest(DomainTestFactory.java:355)

    E.g. thenReturn() may be missing.
    Examples of correct stubbing:
        when(mock.isOk()).thenReturn(true);
        when(mock.isOk()).thenThrow(exception);
        doThrow(exception).when(mock).someVoidMethod();
    Hints:
     1. missing thenReturn()
     2. you are trying to stub a final method, you naughty developer!

        at a.b.DomainTestFactory.myTest(DomainTestFactory.java:276)
        ..........

DomainTestFactoryรหัสการทดสอบจาก เมื่อฉันรันการทดสอบต่อไปนี้ฉันเห็นข้อยกเว้น

@Test
public myTest(){
    MyMainModel mainModel =  Mockito.mock(MyMainModel.class);
    Mockito.when(mainModel.getList()).thenReturn(getSomeList()); // Line 355
}

private List<SomeModel> getSomeList() {
    SomeModel model = Mockito.mock(SomeModel.class);
    Mockito.when(model.getName()).thenReturn("SomeName"); // Line 276
    Mockito.when(model.getAddress()).thenReturn("Address");
    return Arrays.asList(model);
}

public class SomeModel extends SomeInputModel{
    protected String address;
    protected List<SomeClass> properties;

    public SomeModel() {
        this.Properties = new java.util.ArrayList<SomeClass>(); 
    }

    public String getAddress() {
        return this.address;
    }

}

public class SomeInputModel{

    public NetworkInputModel() {
        this.Properties = new java.util.ArrayList<SomeClass>(); 
    }

    protected String Name;
    protected List<SomeClass> properties;

    public String getName() {
        return this.Name;
    }

    public void setName(String value) {
        this.Name = value;
    }
}

สวัสดี Mureinik ฉันได้อัปเดตโพสต์ด้วยหมายเลขบรรทัด
Royal Rose

คำตอบ:


371

คุณกำลังทำรังในที่เยาะเย้ยถากถาง คุณกำลังโทรหาgetSomeList()ซึ่งจะทำการเยาะเย้ยMyMainModelซึ่งจะเยาะเย้ยบางส่วนก่อนที่จะเสร็จสิ้นการเยาะเย้ยสำหรับMockito ไม่ชอบเมื่อคุณทำเช่นนี้

แทนที่

@Test
public myTest(){
    MyMainModel mainModel =  Mockito.mock(MyMainModel.class);
    Mockito.when(mainModel.getList()).thenReturn(getSomeList()); --> Line 355
}

กับ

@Test
public myTest(){
    MyMainModel mainModel =  Mockito.mock(MyMainModel.class);
    List<SomeModel> someModelList = getSomeList();
    Mockito.when(mainModel.getList()).thenReturn(someModelList);
}

เพื่อให้เข้าใจว่าทำไมสิ่งนี้จึงทำให้เกิดปัญหาคุณจำเป็นต้องรู้เพียงเล็กน้อยเกี่ยวกับวิธีการทำงานของ Mockito และยังต้องรับรู้ในการแสดงออกของคำสั่งและคำสั่งใดที่ประเมินใน Java

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

เส้น

Mockito.when(mainModel.getList()).thenReturn(someModelList);

ทำให้เกิดการโต้ตอบกับ Mockito ต่อไปนี้:

  • วิธีการจำลอง mainModel.getList()เรียกว่า
  • วิธีการแบบคงที่whenเรียกว่า
  • เมธอดthenReturnถูกเรียกบนOngoingStubbingอ็อบเจ็กต์ที่ส่งคืนโดยwhenเมธอด

thenReturnวิธีแล้วสามารถสั่งจำลองที่ได้รับผ่านทางOngoingStubbingวิธีการที่จะจัดการกับการเรียกร้องใด ๆ ที่เหมาะสมกับวิธีการที่จะกลับมาgetListsomeModelList

ในความเป็นจริงเนื่องจาก Mockito ไม่เห็นรหัสของคุณคุณสามารถเขียนการเยาะเย้ยได้ดังนี้

mainModel.getList();
Mockito.when((List<SomeModel>)null).thenReturn(someModelList);

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

อย่างไรก็ตามสาย

Mockito.when(mainModel.getList()).thenReturn(getSomeList());

ทำให้เกิดการโต้ตอบกับ Mockito ต่อไปนี้:

  1. วิธีการจำลอง mainModel.getList()เรียกว่า
  2. วิธีการคงที่ whenเรียกว่า
  3. มีการสร้างใหม่mockของSomeModel(ภายในgetSomeList() )
  4. วิธีการจำลองmodel.getName()เรียกว่า

ณ จุดนี้ Mockito สับสน มันคิดว่าคุณกำลังล้อเลียนmainModel.getList()แต่ตอนนี้คุณกำลังบอกว่าคุณต้องการจำลองmodel.getName()วิธีการ สำหรับ Mockito ดูเหมือนว่าคุณกำลังทำสิ่งต่อไปนี้:

when(mainModel.getList());
// ...
when(model.getName()).thenReturn(...);

มันดูโง่ไปMockitoเพราะมันไม่แน่ใจว่าคุณกำลังทำอะไรmainModel.getList()อยู่

โปรดทราบว่าเราไม่ได้รับthenReturnการเรียกใช้เมธอดเนื่องจาก JVM จำเป็นต้องประเมินพารามิเตอร์ของเมธอดนี้ก่อนที่จะสามารถเรียกเมธอดได้ ในกรณีนี้หมายถึงการเรียกใช้getSomeList()เมธอด

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

สุดท้าย Mockito เวอร์ชันล่าสุดจะเพิ่มบรรทัดพิเศษลงในข้อความแสดงข้อผิดพลาดด้านบน บรรทัดพิเศษนี้แสดงว่าคุณอาจอยู่ในสถานการณ์เดียวกันกับคำถามนี้:

3: คุณกำลังขัดจังหวะพฤติกรรมของการเยาะเย้ยอื่นภายในคำสั่ง 'then return' ถ้าเสร็จแล้ว


มีคำอธิบายของข้อเท็จจริงนี้หรือไม่? โซลูชันทำงานได้ดี และฉันไม่เข้าใจว่าทำไมการสร้างแบบจำลองในสถานที่จึงไม่ได้ผล เมื่อคุณสร้างจำลองและส่งจำลองที่สร้างขึ้นโดยการอ้างอิงกับจำลองอื่น ๆ มันจะทำงาน
Capacytron

1
คำตอบที่ยอดเยี่ยมรักมาก ๆ ! ฉันคงต้องใช้เวลานานมากในการค้นหาสิ่งนี้ด้วยตัวเอง
Dici

4
คำตอบที่ยอดเยี่ยมลุค! คำอธิบายโดยละเอียดอย่างมากในคำง่าย ๆ ขอบคุณ.
Tomasz Kalkosiński

1
น่ากลัว สิ่งที่ตลกคือเมื่อฉันทำการเรียกเมธอดโดยตรงและดีบักช้าๆมันก็ใช้ได้ คุณสมบัติของ CGLIB $ BOUND จะได้รับค่าจริง แต่อย่างใดก็ใช้เวลาเล็กน้อย เมื่อฉันใช้การเรียกเมธอดโดยตรงและหยุดก่อนการฝึกอบรม (เมื่อ ... ) จากนั้นฉันเห็นว่าค่าเป็นเท็จครั้งแรกและกลายเป็นจริงในภายหลัง เมื่อเป็นเท็จและการฝึกอบรมเริ่มขึ้นข้อยกเว้นนี้จะเกิดขึ้น
Michael Hegner

คุณทำวันของฉัน! นี่เป็นข้อผิดพลาดที่ทำให้คุณเสียเวลามาก! ฉันคิดว่ามันเป็นสิ่งที่เกี่ยวข้องกับ kotlin ในตอนเริ่มต้น
บรองซ์

1
org.mockito.exceptions.misusing.UnfinishedStubbingException: 
Unfinished stubbing detected here:
E.g. thenReturn() may be missing.

สำหรับการเยาะเย้ยของวิธีการโมฆะลองด้านล่าง:

//Kotlin Syntax

 Mockito.`when`(voidMethodCall())
           .then {
                Unit //Do Nothing
            }
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.