Незакінчена заглушка виявлена ​​в Мокіто


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

Привіт Мурейник, я оновив пост з номерами рядків
Royal Rose

Відповіді:


371

Ви гніздите глузування всередині глузування. Ви зателефонували getSomeList(), що змушує насміхатися, перш ніж ви закінчите знущатися над MyMainModel. Мокіто не сподобається, коли ти це робиш.

Замініть

@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метод, обробляти будь-який відповідний виклик getListметоду для повернення someModelList.

Насправді, оскільки 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()називається,

У цей момент Мокіто плутається. Вам здалося, що ви знущаєтесь mainModel.getList(), але тепер ви говорите йому, що хочете знущатися над model.getName()методом. Для Mockito, схоже, ви робите наступне:

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

Це виглядає нерозумно, Mockitoоскільки він не може бути впевнений, що ти робиш mainModel.getList().

Зауважте, що ми не потрапили до thenReturnвиклику методу, оскільки JVM повинен оцінити параметри цього методу, перш ніж він може викликати метод. У цьому випадку це означає викликати getSomeList()метод.

Як правило, це погане дизайнерське рішення покладатися на статичний стан, як це робить Мокіто, оскільки це може призвести до випадків, коли порушується Принцип найменшого здивування. Однак дизайн Mockito робить чіткий та виразний глузування, навіть якщо іноді це призводить до здивування.

Нарешті, останні версії Mockito додають додатковий рядок до повідомлення про помилку вище. Цей додатковий рядок означає, що ви можете опинитися в тій же ситуації, що і це питання:

3: ви заглушуєте поведінку іншого макету всередині перед інструкцією "thenReturn", якщо її завершено


Чи є якесь пояснення цього факту? Рішення працює. І я не розумію, чому створюється макет "на місці" не працює. Коли ви створюєте макет і передаєте створений макет посиланням на інший макет, він працює.
Капацитрон

1
Відмінна відповідь, люблю ТАК! Знадобилося б мені віки, щоб знайти це самостійно
зрозумів

4
Чудова відповідь Лука! Дуже детальне пояснення простими словами. Дякую.
Томаш Калкосінський

1
Дивовижно. Найсмішніше, що коли я виконую прямий виклик методу та налагоджую повільно, тоді він працює. Атрибут CGLIB $ BOUND отримає значення істинним, але якось це займе небагато часу. Коли я використовую прямий виклик методу і зупиняюсь перед тренуванням (коли ...), то я бачу, що значення спочатку помиляється, а згодом стає істинним. Коли це неправда і тренування починаються, тоді цей виняток виникає.
Майкл Хегнер

Ти зробив мій день! Це така помилка, яка змушує витрачати багато часу! Я подумав, що це було щось пов’язане з котліном на початку
Бронкс,

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
            }
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.