Недавно я обнаружил, что пишу код для интеграции двух разнородных платформ. Одна из этих систем основана на Java, а другая, хотя и не написана на Java, предлагает Java API. Я назову эти системы Foo и Bar соответственно.
Однако до того, как я написал строку кода, стало очевидно, что для тестирования возможного адаптера потребуется явная насмешка над более поздним системным API (то есть с Foo), поскольку все, что мне нужно было, это jar-файл, классы и методы которого давали понять они общались с живым экземпляром.
Я провел несколько циклов, чтобы увидеть, что нового в мире Java-издевательств, и мне было приятно видеть, что мой старый друг Mockito все еще активен и действительно, по-прежнему, отличный инструмент для издевательств общего назначения. Для непосвященных, Mockito — это основанная на Java фреймворк, который:
… очень вкусно. Это позволяет вам писать красивые тесты с помощью простого и понятного API. Mockito не дает вам похмелья, потому что тесты очень удобочитаемы и выдают чистые ошибки проверки.
Страница проекта Google code mockito — зачем пить?
Действительно, Mockito предлагает простой и удобный API, который позволяет точно высмеивать поведение без особых хлопот. Например, основным фасадом для взаимодействия с Bar является класс QTP
котором есть такие методы, как logIn
, logOut
и т. Д. Вместо того, чтобы полагаться на тестируемые классы для фактического вызова этих методов, я легко могу создать фиктивные экземпляры QTP
с помощью Mockito, например: :
Дразнить экземпляр QTP
1
|
QTP qtpThing = mock(QTP. class ); |
Где mock
— это статически импортированный метод из org.mockito.Mockito
. С помощью фиктивного экземпляра я могу затем диктовать, как я хочу, чтобы некоторые методы вели себя при условии, что я передаю этот имитированный экземпляр моим тестируемым классам .
Например, метод logIn
ничего не возвращает; на самом деле, сначала нужно вызвать этот метод, а затем вызвать другой метод для генерации тикета (или токена), который будет использоваться при последующих вызовах методов. Таким образом, адаптер, который я пишу, получит некоторые входные значения (от Foo в форме XML), а адаптер вернет билет (в форме документа XML в соответствии с требуемой схемой XML Foo).
Соответственно, для проверки этого взаимодействия мне нужно сделать две вещи:
- убедитесь, что метод
logIn
был вызван с определенными параметрами -
getTicket
ответ действительного билета, используя методgetTicket
Более того, я также хотел бы убедиться, что сбой logIn
в logIn
приводит к определенному взаимодействию из кода моего адаптера. Поэтому мне нужно будет высказать и какое-то исключительное поведение.
В случае макетирования конкретного метода вы просто объединяете несколько методов; в моем случае, when
и тогда thenReturn
делают трюк так:
Дразнить поведение getTicket
1
|
when(qtpThing.getTicket()).thenReturn( "test-ticket" ); |
В приведенном выше коде, когда метод getTicket
вызывается в моем фиктивном экземпляре, возвращается String
«test-ticket».
Затем, чтобы гарантировать, что logIn
был вызван с параметрами, полученными из входящего XML-документа, я могу использовать метод verify
Mockito.
Использование проверки Mockito для обеспечения правильного взаимодействия
1
|
verify(qtpThing, times( 1 )).logIn( "some_value" , "some_user_name" , "password" ); |
В этом случае метод logIn
проверяет, что logIn
вызывается один раз, и что передаются три конкретных значения String
. Если эти ожидания не будут выполнены, Mockito сгенерирует исключение (и ваш соответствующий тестовый пример потерпит неудачу).
Таким образом, мой тестовый пример для проверки моего адаптера довольно прост, но очень удобочитаемый.
Контрольный пример JUnit для проверки поведения входа
01
02
03
04
05
06
07
08
09
10
11
12
|
@Test public void testLoginRequest() throws Exception { QTP qtpThing = mock(QTP. class ); when(qtpThing.getTicket()).thenReturn( "test-ticket" ); AdapterRequest request = new AdapterRequest(XML.read( "etc/test-login-req.xml" )); QbosAdapter adapter = new QbosAdapter(); adapter.setQtpInstance(qtpThing); AdapterResponse adapterResponse = adapter.performAction(request); assertNotNull(adapterResponse); verify(qtpThing, times( 1 )).logIn( "some_value" , "some_user_name" , "password" ); assertEquals( "test-ticket" , adapterResponse.getData().getText()); } |
Что если мне нужно смоделировать исключение, генерируемое объектом QTP
, якобы из недопустимого параметра или неверных учетных данных во время входа в систему? Опять же, свободный API Mockito делает это бризом.
В моем случае я бы хотел, logIn
метод logIn
выбрасывал один из проверенных методов в сигнатуре его метода с именем UnknownQtpException
. Вы можете сделать это с помощью методов doThrow
и when
.
Макетирование исключений в Мокито
1
|
doThrow( new UnknownQtpException()).when(qtpThing).logIn( "" , "blah" , "blah" ); |
В приведенном выше коде я явно logIn
что если первый параметр команды logIn
пуст, то мой QTP
экземпляр QTP
должен UnknownQtpException
. Собрав все вместе, получим следующий тестовый пример:
Тестирование исключительных обстоятельств с JUnit & Mockito
01
02
03
04
05
06
07
08
09
10
11
12
13
|
@Test public void testFailureLoginRequest() throws Exception { QTP qtpThing = mock(QTP. class ); doThrow( new UnknownQtpException()).when(qtpThing).logIn( "" , "blah" , "blah" ); XML xml = XML.read( "etc/test-login-req-err.xml" ); AdapterRequest request = new AdapterRequest(xml); QbosAdapter adapter = new QbosAdapter(); adapter.setQtpInstance(qtpThing); AdapterResponse adapterResponse = adapter.performAction(request); assertNotNull(adapterResponse); verify(qtpThing, times( 1 )).logIn( "" , "blah" , "blah" ); assertEquals( "FAILURE" , adapterResponse.getData().getText()); } |
Прелесть, конечно же, состоит в том, что мои тесты эффективно тестируют мой код адаптера, не полагаясь на стороннюю систему (в данном случае Bar). Это, естественно, проверенная временем техника тестирования, которую можно использовать на любом языке, с достойной насмешки!
Если вы обнаружите, что пишете код интеграции на Java, то я не могу рекомендовать Mockito. API Mockito довольно прост и облегчает понимание тестов. Я имею в виду, что тесты легко усваиваются. Копать это?