Как высмеять окончательный class с mockito

У меня есть последний class, что-то вроде этого:

public final class RainOnTrees{ public void startRain(){ // some code here } } 

Я использую этот class в каком-то другом classе следующим образом:

 public class Seasons{ RainOnTrees rain = new RainOnTrees(); public void findSeasonAndRain(){ rain.startRain(); } } 

и в моем тестовом classе JUnit для Seasons.java Я хочу RainOnTrees class RainOnTrees . Как я могу это сделать с Mockito?

Мокширование финальных / статических classов / методов возможно только с Mockito v2.

Это невозможно с Mockito v1, из FAQ Mockito :

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

  • Требуется java 1.5+

  • Нельзя издеваться над финальными classами

Mockito 2 теперь поддерживает финальные classы и методы!

Но пока это «инкубационная» функция. Для его активации требуются некоторые шаги, которые описаны в « Что нового в Mockito 2 :

Издевательство над финальными classами и методами – это инкубация , возможность выбора. Он использует комбинацию инструментария и подclassа Java-агента, чтобы включить имитацию этих типов. Поскольку это работает по-другому с нашим текущим механизмом, и у этого есть разные ограничения, и поскольку мы хотим собрать опыт и отзывы пользователей, эта функция должна быть явно активирована, чтобы быть доступной; это можно сделать с помощью механизма расширения mockito, создав файл src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker содержащий одну строку:

 mock-maker-inline 

После создания этого файла Mockito будет автоматически использовать этот новый движок, и вы сможете:

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

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

Вы не можете издеваться над финальным classом с Mockito, так как вы не можете сделать это самостоятельно.

То, что я делаю, заключается в создании не конечного classа для обертывания последнего classа и использования его в качестве делегата. Примером этого является class TwitterFactory , и это мой classный class:

 public class TwitterFactory { private final twitter4j.TwitterFactory factory; public TwitterFactory() { factory = new twitter4j.TwitterFactory(); } public Twitter getInstance(User user) { return factory.getInstance(accessToken(user)); } private AccessToken accessToken(User user) { return new AccessToken(user.getAccessToken(), user.getAccessTokenSecret()); } public Twitter getInstance() { return factory.getInstance(); } } 

Недостатком является то, что существует много шаблонов кода; преимущество заключается в том, что вы можете добавить некоторые методы, которые могут относиться к вашей бизнес-приложениям (например, getInstance, который принимает пользователя, а не accessToken, в приведенном выше случае).

В вашем случае я бы создал RainOnTrees class RainOnTrees который делегирует конечный class. Или, если вы можете сделать это не финальным, было бы лучше.

Используйте Powermock. Эта ссылка показывает, как это сделать: https://github.com/jayway/powermock/wiki/MockFinal

Просто следить. Добавьте эту строку в свой файл gradle:

 testCompile group: 'org.mockito', name: 'mockito-inline', version: '2.8.9' 

Я пробовал различную версию mockito-core и mockito-all. Ни один из них не работает.

добавьте это в свой файл gradle:

 testCompile 'org.mockito:mockito-inline:2.13.0' 

это настройка для работы mockito с заключительными classами

У меня такая же проблема. Поскольку class, который я пытался высмеять, был простым classом, я просто создал его экземпляр и вернул его.

Я думаю, вы сделали это final потому что хотите запретить другим classам распространять RainOnTrees . Как предлагает эффективная Java (пункт 15), есть еще один способ сохранить class для расширения, не делая его final :

  1. Удалить ключевое слово final ;

  2. Сделайте свой конструктор private . Ни один class не сможет расширить его, потому что он не сможет вызвать super конструктор;

  3. Создайте статический заводский метод для создания экземпляра вашего classа.

     // No more final keyword here. public class RainOnTrees { public static RainOnTrees newInstance() { return new RainOnTrees(); } private RainOnTrees() { // Private constructor. } public void startRain() { // some code here } } 

Используя эту страtagsю, вы сможете использовать Mockito и держать свой class закрытым для расширения с небольшим шаблоном кода.

Попробуйте:

 Mockito.mock(SomeMockableType.class,AdditionalAnswers.delegatesTo(someInstanceThatIsNotMockableOrSpyable)); 

Это сработало для меня. «SomeMockableType.class» – это родительский class того, что вы хотите насмехаться или шпионить, а someInstanceThatIsNotMockableOrSpyable – это фактический class, который вы хотите издеваться или шпионить.

Для более подробной информации смотрите здесь

Другим обходным решением, которое может применяться в некоторых случаях, является создание интерфейса, который реализуется этим окончательным classом, изменить код, чтобы использовать интерфейс вместо конкретного classа, а затем издеваться над интерфейсом. Это позволяет отделить контракт (интерфейс) от реализации (конечный class). Конечно, если вы действительно хотите привязываться к конечному classу, это не будет применяться.

На самом деле есть один способ, который я использую для шпионажа. Это будет работать для вас только в том случае, если будут удовлетворены две предпосылки:

  1. Вы используете какой-то DI, чтобы ввести экземпляр конечного classа
  2. Заключительный class реализует интерфейс

Пожалуйста, вспомните пункт 16 из « Эффективной Java» . Вы можете создать оболочку (не окончательную) и переадресовать весь вызов экземпляру конечного classа:

 public final class RainOnTrees implement IRainOnTrees { @Override public void startRain() { // some code here } } public class RainOnTreesWrapper implement IRainOnTrees { private IRainOnTrees delegate; public RainOnTreesWrapper(IRainOnTrees delegate) {this.delegate = delegate;} @Override public void startRain() { delegate.startRain(); } } 

Теперь вы можете не только высмеять свой последний class, но и шпионить за ним:

 public class Seasons{ RainOnTrees rain; public Seasons(IRainOnTrees rain) { this.rain = rain; }; public void findSeasonAndRain(){ rain.startRain(); } } IRainOnTrees rain = spy(new RainOnTreesWrapper(new RainOnTrees()) // or mock(IRainOnTrees.class) doNothing().when(rain).startRain(); new Seasons(rain).findSeasonAndRain(); , public class Seasons{ RainOnTrees rain; public Seasons(IRainOnTrees rain) { this.rain = rain; }; public void findSeasonAndRain(){ rain.startRain(); } } IRainOnTrees rain = spy(new RainOnTreesWrapper(new RainOnTrees()) // or mock(IRainOnTrees.class) doNothing().when(rain).startRain(); new Seasons(rain).findSeasonAndRain(); 

Да, такая же проблема здесь, мы не можем издеваться над финальным classом с Mockito. Чтобы быть точным, Мокито не может насмехаться / шпионить за:

  • заключительные classы
  • анонимные classы
  • примитивные типы

Но использование classа-оболочки представляется мне большой ценой, поэтому вместо этого получите PowerMockito.

Это можно сделать, если вы используете Mockito2, с новой функцией инкубации, которая поддерживает насмешку над окончательными classами и методами.

Ключевые моменты:
1. Создайте простой файл с именем «org.mockito.plugins.MockMaker» и поместите его в папку с именем «mockito-extensions». Эта папка должна быть доступна в пути к classам.
2. Содержимое созданного файла должно быть одной строкой, как указано ниже:
макет-мейкера-рядный

Вышеуказанные два шага необходимы для активации механизма расширения mockito и использования этой функции выбора.

Примеры classов: –

FinalClass.java

 public final class FinalClass { public final String hello(){ System.out.println("Final class says Hello!!!"); return "0"; } 

}

Foo.java

 public class Foo { public String executeFinal(FinalClass finalClass){ return finalClass.hello(); } 

}

FooTest.java

 public class FooTest { @Test public void testFinalClass(){ // Instantiate the class under test. Foo foo = new Foo(); // Instantiate the external dependency FinalClass realFinalClass = new FinalClass(); // Create mock object for the final class. FinalClass mockedFinalClass = mock(FinalClass.class); // Provide stub for mocked object. when(mockedFinalClass.hello()).thenReturn("1"); // assert assertEquals("0", foo.executeFinal(realFinalClass)); assertEquals("1", foo.executeFinal(mockedFinalClass)); } 

}

Надеюсь, поможет.

Полная статья, представленная здесь, насмешливо-немодущая .

Посмотрите на JMockit . Он имеет обширную документацию с большим количеством примеров. Здесь у вас есть пример решения вашей проблемы (чтобы упростить, я добавил конструктор в Seasons чтобы ввести RainOnTrees экземпляр RainOnTrees ):

 package jmockitexample; import mockit.Mocked; import mockit.Verifications; import mockit.integration.junit4.JMockit; import org.junit.Test; import org.junit.runner.RunWith; @RunWith(JMockit.class) public class SeasonsTest { @Test public void shouldStartRain(@Mocked final RainOnTrees rain) { Seasons seasons = new Seasons(rain); seasons.findSeasonAndRain(); new Verifications() {{ rain.startRain(); }}; } public final class RainOnTrees { public void startRain() { // some code here } } public class Seasons { private final RainOnTrees rain; public Seasons(RainOnTrees rain) { this.rain = rain; } public void findSeasonAndRain() { rain.startRain(); } } } 

Решения, предоставленные RC и Luigi R. Viggiano вместе, возможно, лучшая идея.

Хотя Мокито не может , по дизайну, высмеивать заключительные classы, подход делегации возможен . Это имеет свои преимущества:

  1. Вы не обязаны менять свой class на не-окончательный, если это то, что ваш API намеревается в первую очередь (у конечных classов есть свои преимущества ).
  2. Вы проверяете возможность украшения вокруг вашего API.

В тестовом случае вы намеренно перенаправляете вызовы в тестируемую систему. Следовательно, по дизайну ваше украшение ничего не делает.

Следовательно, вы можете продемонстрировать, что пользователь может только украсить API, а не расширять его.

В более субъективной заметке: я предпочитаю свести frameworks к минимуму, поэтому для меня обычно достаточно JUnit и Mockito. Фактически, ограничение этого способа иногда заставляет меня реорганизовать и навсегда.

Как заявили другие, это не будет работать из коробки с Мокито. Я бы предложил использовать reflection, чтобы установить конкретные поля объекта, который используется тестируемым кодом. Если вы обнаружите, что делаете это много, вы можете обернуть эту функциональность в библиотеку.

В стороне, если вы являетесь одним из classов маркировки, перестаньте это делать. Я столкнулся с этим вопросом, потому что я работаю с API, где все было помечено как окончательное, чтобы предотвратить мою законную потребность в расширении (насмешка), и я хочу, чтобы разработчик не предполагал, что мне не нужно будет расширять class.

Хранитель времени для людей, которые сталкиваются с одной проблемой (Mockito + Final Class) на Android + Kotlin. Как и в classах Котлина, окончательные по умолчанию. Я нашел решение в одном из образцов Google Android с компонентом Architecture. Решение выбрано здесь: https://github.com/googlesamples/android-architecture-components/blob/master/GithubBrowserSample

Создайте следующие annotations:

 /** * This annotation allows us to open some classes for mocking purposes while they are final in * release builds. */ @Target(AnnotationTarget.ANNOTATION_CLASS) annotation class OpenClass /** * Annotate a class with [OpenForTesting] if you want it to be extendable in debug builds. */ @OpenClass @Target(AnnotationTarget.CLASS) annotation class OpenForTesting 

Измените файл gradleиента. Пример отсюда: https://github.com/googlesamples/android-architecture-components/blob/master/GithubBrowserSample/app/build.gradle

 apply plugin: 'kotlin-allopen' allOpen { // allows mocking for classes w/o directly opening them for release builds annotation 'com.android.example.github.testing.OpenClass' } 

Теперь вы можете аннотировать любой class, чтобы он был открыт для тестирования:

 @OpenForTesting class RepoRepository 

Не пробовал финал, но для частного, используя reflection, удалял модификатор! проверили дальше, он не работает для финала.

  • Как проверить метод, вызывается два раза с помощью mockito verify ()
  • Как использовать Mockito, когда мы не можем передать макет объекта экземпляру classа
  • Как правильно сопоставить varargs в Mockito
  • Mockito: проверка с помощью общих параметров
  • Mockito: Пытаться шпионить за методом вызывает оригинальный метод
  • Мокито, Юнит и Весна
  • @RunWith (MockitoJUnitRunner.class) против MockitoAnnotations.initMocks (это)
  • Инъекция Мокито издевается над весенним бобаном
  • Как сделать издевательство над недействительными методами с mockito
  • Инициализация ложных объектов - MockIto
  • В чем разница между насмешкой и шпионажем при использовании Mockito?
  • Давайте будем гением компьютера.