Финальный метод издевательства - PullRequest
57 голосов
/ 25 сентября 2010

Мне нужно смоделировать некоторый класс с помощью финального метода, использующего 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());
    }

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

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

Спасибо.

Ответы [ 7 ]

32 голосов
/ 25 сентября 2010

Из FAQ по Mockito :

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

  • Не удается издеваться над конечными методами - ихРеальное поведение выполняется без каких-либо исключений.Мокито не может предупредить вас о насмешливых окончательных методах, поэтому будьте бдительны.
29 голосов
/ 25 сентября 2010

В Mockito нет поддержки для насмешки над финальными методами.

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

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

19 голосов
/ 20 сентября 2012

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

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

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

14 голосов
/ 09 октября 2016

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

4 голосов
/ 18 декабря 2018

Mockito 2.x теперь поддерживает метод final и последний класс.

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

Насмешка над итоговыми уроками и методами - инкубационная функция. Эту функцию необходимо явно активировать, создав файл 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());

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

3 голосов
/ 25 апреля 2017

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

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

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

@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());

}
0 голосов
/ 01 октября 2013

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

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

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

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

...