Stubbing chưa hoàn thành được phát hiện ở Mockito


151

Tôi đang nhận được ngoại lệ trong khi chạy thử nghiệm. Tôi đang sử dụng Mockito để chế giễu. Các gợi ý được đề cập bởi thư viện Mockito không giúp được gì.

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)
        ..........

Mã kiểm tra từ DomainTestFactory. Khi tôi chạy thử nghiệm sau, tôi thấy ngoại lệ.

@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;
    }
}

Xin chào Mureinik, tôi đã cập nhật bài đăng với số dòng
Royal Rose

Câu trả lời:


371

Bạn đang làm tổ bên trong chế giễu. Bạn đang gọi getSomeList(), một số chế nhạo, trước khi bạn kết thúc chế độ chế giễu MyMainModel. Mockito không thích nó khi bạn làm điều này.

Thay thế

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

với

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

Để hiểu lý do tại sao điều này gây ra vấn đề, bạn cần biết một chút về cách Mockito hoạt động và cũng cần lưu ý về các biểu thức và câu lệnh được đánh giá trong Java.

Mockito không thể đọc mã nguồn của bạn, vì vậy để tìm ra những gì bạn đang yêu cầu, nó phụ thuộc rất nhiều vào trạng thái tĩnh. Khi bạn gọi một phương thức trên một đối tượng giả, Mockito ghi lại các chi tiết của cuộc gọi trong một danh sách nội bộ của các lệnh. Các whenphương pháp đọc cuối cùng của những lời gọi ra khỏi danh sách và ghi lại gọi này trong OngoingStubbingđối tượng nó sẽ trả về.

Dòng

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

gây ra các tương tác sau với Mockito:

  • Phương pháp giả mainModel.getList()được gọi là,
  • Phương thức tĩnh whenđược gọi là,
  • Phương thức thenReturnđược gọi trên OngoingStubbingđối tượng được trả về bởi whenphương thức.

Các thenReturnthì phương pháp có thể hướng dẫn các mô hình đã nhận được thông qua các OngoingStubbingphương pháp để xử lý bất kỳ cuộc gọi phù hợp với getListphương pháp để trở lại someModelList.

Thực tế, vì Mockito không thể nhìn thấy mã của bạn, bạn cũng có thể viết lời chế giễu của mình như sau:

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

Phong cách này có phần ít rõ ràng hơn để đọc, đặc biệt là trong trường hợp nullnày phải được đúc, nhưng nó tạo ra chuỗi tương tác tương tự với Mockito và sẽ đạt được kết quả tương tự như dòng trên.

Tuy nhiên, dòng

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

gây ra các tương tác sau với Mockito:

  1. Phương pháp giả mainModel.getList()được gọi là,
  2. Phương thức tĩnh whenđược gọi là,
  3. Một mới mockcủa SomeModelđược tạo ra (bên trong getSomeList()),
  4. Phương pháp giả model.getName()được gọi là,

Lúc này Mockito bị nhầm lẫn. Nó nghĩ rằng bạn đang chế giễu mainModel.getList(), nhưng bây giờ bạn đang nói với nó rằng bạn muốn chế giễu model.getName()phương pháp này. Đối với Mockito, có vẻ như bạn đang làm như sau:

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

Điều này có vẻ ngớ ngẩn Mockitovì nó không thể chắc chắn những gì bạn đang làm với mainModel.getList().

Lưu ý rằng chúng ta không nhận được thenReturncuộc gọi phương thức, vì JVM cần đánh giá các tham số cho phương thức này trước khi nó có thể gọi phương thức. Trong trường hợp này, điều này có nghĩa là gọi getSomeList()phương thức.

Nói chung, đó là một quyết định thiết kế tồi khi dựa vào trạng thái tĩnh, như Mockito, bởi vì nó có thể dẫn đến các trường hợp Nguyên tắc tối thiểu bị vi phạm. Tuy nhiên, thiết kế của Mockito không tạo ra sự chế giễu rõ ràng và biểu cảm, ngay cả khi đôi khi nó dẫn đến sự ngạc nhiên.

Cuối cùng, các phiên bản gần đây của Mockito thêm một dòng bổ sung vào thông báo lỗi ở trên. Dòng bổ sung này cho biết bạn có thể ở trong tình huống tương tự như câu hỏi này:

3: bạn đang ngăn chặn hành vi của một người giả khác bên trong trước khi hướng dẫn 'thenReturn' nếu hoàn thành


Có bất kỳ lời giải thích về thực tế này? Giải pháp hoạt động. Và tôi không hiểu tại sao sáng tạo giả 'tại chỗ' không hoạt động. Khi bạn tạo giả và vượt qua tạo giả bằng cách tham chiếu đến giả khác, nó hoạt động.
Capacytron

1
Câu trả lời tuyệt vời, tình yêu SO! Tôi sẽ mất nhiều thời gian để tự mình tìm thấy điều này
Dici

4
Luke trả lời tuyệt vời! Giải thích rất chi tiết bằng những từ đơn giản. Cảm ơn bạn.
Tomasz Kalkosiński

1
Tuyệt vời. Điều buồn cười là, khi tôi thực hiện cuộc gọi phương thức trực tiếp và gỡ lỗi từ từ, thì nó hoạt động. Thuộc tính của CGLIB $ BOUND sẽ nhận được giá trị đúng, nhưng bằng cách nào đó, nó cần một ít thời gian. Khi tôi sử dụng phương thức gọi trực tiếp và dừng trước khi đào tạo (khi ...), thì tôi thấy giá trị này là sai trước tiên và sau đó trở thành đúng. Khi nó là sai và đào tạo bắt đầu, thì ngoại lệ này xảy ra.
Michael Hegner

Bạn đã làm cho ngày của tôi! Đây là loại lỗi khiến bạn lãng phí rất nhiều thời gian! Tôi đã nghĩ rằng đó là một cái gì đó liên quan đến kotlin ngay từ đầu
Bronx

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

Để chế nhạo các phương thức void, hãy thử dưới đây:

//Kotlin Syntax

 Mockito.`when`(voidMethodCall())
           .then {
                Unit //Do Nothing
            }
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.