Вопрос по unit-testing, mocking, mockito – Как работает вызов mockito when ()?

93

Учитывая следующее высказывание Мокито:

when(mock.method()).thenReturn(someValue);

Как в Mockito создаются прокси-объекты для макета, если оператор mock.method () передаст возвращаемое значение в when ()? Я полагаю, что для этого используются некоторые вещи CGLib, но было бы интересно узнать, как это технически сделано.

Переименован для наглядности. Paul Morie

Ваш Ответ

2   ответа
94

mock.method() будет подходящим для типа пустым значением; mockito использует косвенное обращение через прокси, перехват методов и общий экземплярMockingProgress класс, чтобы определить, предназначен ли вызов метода для макета для заглушки или воспроизведения существующего поведения заглушки, а не для передачи информации о заглушке через возвращаемое значение смоделированного метода.

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

Во-первых, когда вы издеваетесь над классом, используяmock методMockito класс, это по сути то, что происходит:

Mockito.mock делегатыorg.mockito.internal.MockitoCore.mock, передавая параметры макета по умолчанию в качестве параметра.MockitoCore.mock делегатыorg.mockito.internal.util.MockUtil.createMockMockUtil класс используетClassPathLoader класс, чтобы получить экземплярMockMaker использовать для создания макета. По умолчаниюCgLibMockMaker класс используется.CgLibMockMaker использует класс, заимствованный из JMock,ClassImposterizer это обрабатывает создание макета. Ключевыми элементами использованной «магии мокито» являютсяMethodInterceptor используется для создания макета: мокитоMethodInterceptorFilterи цепочка экземпляров MockHandler, включая экземплярMockHandlerImpl, Перехватчик метода передает вызовы экземпляру MockHandlerImpl, который реализует бизнес-логику, которая должна применяться, когда метод вызывается для макета (т. Е. Поиск, чтобы увидеть, записан ли уже ответ, определение, представляет ли вызов новую заглушку, и т. Д.). Состояние по умолчанию таково: если заглушка еще не зарегистрирована для вызываемого метода, соответствующий типупустой значение возвращается.

Теперь давайте посмотрим на код в вашем примере:

when(mock.method()).thenReturn(someValue)

Вот порядок, в котором этот код будет выполняться:

mock.method()when(<result of step 1>)<result of step 2>.thenReturn

Ключом к пониманию того, что происходит, является то, что происходит, когда вызывается метод на макете: перехватчику метода передается информация о вызове метода и делегируется его цепочкеMockHandler экземпляры, которые в конечном итоге делегироватьMockHandlerImpl#handle, В течениеMockHandlerImpl#handleобработчик макета создает экземплярOngoingStubbingImpl и передает его в общийMockingProgress экземпляр.

Когдаwhen метод вызывается после вызоваmethod()он делегируетMockitoCore.when, который называетstub() метод того же класса. Этот метод распаковывает текущую заглушку из общегоMockingProgress экземпляр, который издевалсяmethod() вызов записал, и возвращает его. затемthenReturn Затем метод вызывается наOngoingStubbing экземпляр.

Я еще не массировал свое описание здесь, но я не забыл и об этом. FYI. Paul Morie
Спасибо за подробный ответ. Другой вопрос - вы упоминаете, что «когда метод вызывается после вызова метода ()» - как он узнает, что вызов когда () является самым следующим (или переносит) вызов метода ()? Надеюсь, что это имеет смысл. marchaos
Я бы сказал, что вкратце проще перехватить вызов метода внутри другого метода с помощью CGLIB или Javassist, чтобы перехватить, скажем, оператор «если». Infeligo
Роджерио, это на самом деле немного более тонко, чем у этого - у mockito нет явных режимов заглушки и воспроизведения. Я отредактирую свой ответ позже, чтобы он был более понятным. Paul Morie
@marchaos Не знает. Сwhen(mock.method()).thenXyz(...) синтаксис,mock.method() выполняется в режиме «воспроизведения», а не в режиме «заглушки». Обычно это исполнениеmock.method() не имеет никакого эффекта, поэтому позже, когдаthenXyz(...) (thenReturn, thenThrow, thenAnswerи т. д.) выполняется, он переходит в режим «заглушки» и затем записывает желаемый результат для вызова этого метода. Rogério
23

за кулисами Mockito использует некие глобальные переменные / хранилище для сохранения информации о шагах построения заглушки метода (вызов метода (), when (), thenReturn () в вашем примере), так что в конечном итоге он может составить карту того, что должно быть возвращено, когда то, что называется, по какому параметру.

Я нашел эту статью очень полезной:Объяснение того, как работает Mock Frameworks на основе прокси (http://blog.rseiler.at/2014/06/explanation-how-proxy-based-mock.html). Автор реализовал демонстрационную среду Mocking, которую я нашел очень хорошим ресурсом для людей, которые хотят выяснить, как работают эти платформы Mocking.

На мой взгляд, это типичное использование Anti-Pattern. Обычно мы должны избегать «побочного эффекта» при реализации метода, то есть метод должен принимать входные данные, выполнять некоторые вычисления и возвращать результат - кроме этого ничего не изменилось. Но Мокито специально нарушает это правило. Его методы хранят кучу информации, кроме того, что возвращают результат: Mockito.anyString (), mockInstance.method (), when (), thenReturn, все они имеют специальный «побочный эффект». Вот почему на первый взгляд фреймворк выглядит как волшебство - мы обычно не пишем такой код. Тем не менее, в случае фреймворковой структуры этот дизайн анти-паттернов является отличным дизайном, поскольку он приводит к очень простому API.

Отличная ссылка.За этим стоит гений: очень простой API, который делает все это очень красивым. Другое хорошее решение заключается в том, что метод when () использует обобщенные значения, так что метод thenReturn () является типобезопасным. David Tonhofer

Похожие вопросы