Финальный метод издевательства

Мне нужно смоделировать некоторый класс с финальным методом, используя mockito. Я написал что-то вроде этого

@Test
public void test() {
    B b = mock(B.class);
    doReturn("bar called").when(b).bar();   
    assertEquals("must be \"overrided\"", "bar called", b.bar());
    //bla-bla
}


class B {
    public final String bar() {
        return "fail";
    }
}

Но это не удается. Я попробовал некоторые "взломать", и это работает.

   @Test
   public void hackTest() {
        class NewB extends B {
            public String barForTest() {
                return bar();
            }
        }
        NewB b = mock(NewB.class);
        doReturn("bar called").when(b).barForTest();
        assertEquals("must be \"overrided\"", "bar called", b.barForTest());
    }

Работает, но "пахнет".

Итак, где правильный путь?

Благодарю.

 Jon Skeet25 сент. 2010 г., 14:41
У вас нет возможности изменить его? Или прокси к нему через другой класс, который реализует интерфейс?
 Jon Skeet25 сент. 2010 г., 14:31
Конечно, в идеале не нужно издеваться над конечными методами. Вы ничего не сказали нам о том, почему вы пытаетесь это сделать. Обычно я стараюсь сохранить свои зависимости от интерфейсов ... есть ли способ использовать интерфейс, который проксирует реальный класс (если вы не можете изменить сам класс)?
 Stan Kurilin25 сент. 2010 г., 14:53
@ Джон Скит: извините, не SWT. Это org.eclipse.draw2d.
 Stan Kurilin25 сент. 2010 г., 14:35
Этот класс из какого-то устаревшего кода.
 Stan Kurilin25 сент. 2010 г., 14:47
@ Джон Скит: Я не могу изменить это. Вы видите, это библиотека (SWT). Есть интерфейс IFigure и реализация - рисунок. Но IFigure не содержит getLocation ().

Ответы на вопрос(7)

но это «инкубационная» функция. Требуется несколько шагов для его активации, которые описаны здесь:https://github.com/mockito/mockito/wiki/What's-new-in-Mockito-2#mock-the-unmockable-opt-in-mocking-of-final-classesmethods

 mike rodent02 дек. 2016 г., 20:56
Спасибо за это. Вы случайно не знаете, каково текущее состояниеverifyИНГfinal методы есть? Очевидно, я заглянул на эту страницу, но она ничего не говорила ...
 WindRider04 дек. 2016 г., 14:38
Я не уверен. Проверьте это:stackoverflow.com/questions/14292863/...

Предполагая, что класс B такой, как показано ниже:

class B {
    private String barValue;
    public final String bar() {
        return barValue;
    }
    public void final setBar(String barValue) {
        this.barValue = barValue;
    }
}

Есть лучший способ сделать это без использования инфраструктуры PowerMockito. Вы можете создать шпиона для своего класса и высмеивать ваш последний метод. Ниже приведен способ сделать это:

@Test
public void test() {

    B b  = new B();
    b.setBar("bar called") //This should the expected output:final_method_bar()
    B spyB = Mockito.spy(b);
    assertEquals("bar called", spyB.bar());

}

Mockito FAQ:

Каковы ограничения Mockito

Не может издеваться над конечными методами - их реальное поведение выполняется без каких-либо исключений. Мокито не может предупредить вас о насмешливых окончательных методах, поэтому будьте бдительны.
 Cypress Frankenfeld17 дек. 2018 г., 23:42
Теперь это только в спискеMockito 1.x Конкретные ограничения github.com/mockito/mockito/wiki/..., Возможно, вы можете издеваться над финальными методами в Mockito 2.x?
 Cypress Frankenfeld18 дек. 2018 г., 17:40
Да, я только что проверил, и теперь вы можете смоделировать финальные методы в Mockito 2.x. Я добавил, как это сделать в ответе:stackoverflow.com/a/53837478/382892

тогда вам не нужно создавать подкласс B.class. Просто добавьте это в начало вашего тестового класса

@RunWith(PowerMockRunner.class)
@PrepareForTest(B.class)

@PrepareForTest инструктирует Powermock на инструмент B.class, чтобы сделать финальный и статический методы надёжными. Недостаток этого подхода заключается в том, что вы должны использовать PowerMockRunner, что исключает использование других тестеров, таких как тестер Spring.

 Jan Zyka25 нояб. 2015 г., 11:29
Это не работает для меня. Добавлен как рекомендовано, но все еще получаетсяNullPointerException таким же образом, когда не используется@RunWith а также@PrepareForTest, Кажется, класс не был замешан по какой-то причине.
 Cedric Reichenbach25 июл. 2018 г., 16:25
Затем вы должны посмеяться сPowerMockito.mockнеMockito.mock.
 Cypress Frankenfeld05 февр. 2019 г., 19:15
Mockito теперь поддерживает финальный метод насмешки в Mockito 2.xgithub.com/mockito/mockito/wiki/...

Из документов:

Насмешка над итоговыми классами и методами - это инкубационная функция. Эта функция должна быть явно активирована путем создания файлаsrc/test/resources/mockito-extensions/org.mockito.plugins.MockMaker содержащий одну строку:

mock-maker-inline

После создания этого файла вы можете сделать:

final class FinalClass {
  final String finalMethod() { return "something"; }
}

FinalClass concrete = new FinalClass(); 

FinalClass mock = mock(FinalClass.class);
given(mock.finalMethod()).willReturn("not anymore");

assertThat(mock.finalMethod()).isNotEqualTo(concrete.finalMethod());

На последующих этапах команда представит программный способ использования этой функции. Мы определим и предоставим поддержку для всех немыслимых сценариев.

 vmaldosan26 февр. 2019 г., 13:52
Я попробовал этот подход сSpringRunner и работал для конкретного варианта использования, который я хотел, но делает все макеты в проектевстраиваемый сломал несколько тестов в проекте, все они запускаются сMockitoJUnitRunner, Я мог бы предоставить более подробную информацию, если кто-то хочет продолжить расследование. Сейчас я подожду, пока эта функциональность не будет включена в кодовую базу Mockito, не изменяя все макеты проекта.

что метод не «вызывает» ошибку, но так как это метод catch / log / return, я не смог проверить его напрямую, не изменив класс.

Я хотел просто издеваться над логгером, который я передал, но что-то насмешливое над интерфейсом «Log», похоже, не сработало, и насмешка над классом вроде «SimpleLog» не сработала, потому что эти методы являются окончательными.

Я закончил тем, что создал анонимный внутренний класс, расширяющий SimpleLog, который переопределяет метод «log (level, string, error)» базового уровня, которому делегируются все остальные, а затем просто ждал вызова с «level» 5.

В общем, расширение класса для поведения не является действительно плохой идеей, может быть предпочтительнее насмешки, если это не слишком сложно.

Решение Вопроса

Как прокомментировал Джон Скит, вы должны искать способ избежать зависимости от финального метода. Тем не менее, есть некоторые выходы через манипулирование байт-кодом (например, с PowerMock)

A сравнение между Mockito и PowerMock объясню все подробно.

 Vladyslav Nikolaiev02 мар. 2018 г., 16:02
Ссылка не работает
 Cypress Frankenfeld05 февр. 2019 г., 19:14
Это устарело. Mockito 2.x теперь поддерживает финальный метод пересмешивания (см. Документацию:github.com/mockito/mockito/wiki/...)

Ваш ответ на вопрос