Перейти к основному содержимому

Мокито против EasyMock против JMockit

· 16 мин. чтения

1. Введение

1.1. Обзор

В этом посте мы поговорим о мокировании : что это такое, зачем его использовать и несколько примеров того, как мокировать один и тот же тестовый пример, используя некоторые из наиболее часто используемых мок-библиотек для Java.

Мы начнем с некоторых формальных/полуформальных определений фиктивных понятий; затем мы представим тестируемый случай, приведем примеры для каждой библиотеки и в конце сделаем некоторые выводы. Выбранные библиотеки — Mockito , EasyMock и JMockit .

Если вы чувствуете, что уже знаете основы насмешек, возможно, вы можете перейти к пункту 2, не читая следующие три пункта.

1.2. Причины использовать макеты

Мы начнем с предположения, что вы уже пишете код, следуя некоторой методологии управляемой разработки, основанной на тестах (TDD, ATDD или BDD). Или просто хотите создать тест для существующего класса, который использует зависимости для достижения своей функциональности.

В любом случае, при модульном тестировании класса мы хотим проверить только его функциональность, а не его зависимости (либо потому, что мы доверяем их реализации, либо потому, что мы будем тестировать ее сами).

Для этого нам нужно предоставить тестируемому объекту замену, которую мы можем контролировать для этой зависимости. Таким образом, мы можем форсировать экстремальные возвращаемые значения, генерацию исключений или просто сократить времязатратные методы до фиксированного возвращаемого значения.

Этой управляемой заменой является mock , и она поможет вам упростить кодирование тестов и сократить время выполнения тестов.

1.3. Макетные концепции и определения

Давайте посмотрим на четыре определения из статьи Мартина Фаулера, которые резюмируют основы, которые каждый должен знать о моках:

  • Объекты -пустышки передаются по кругу, но фактически никогда не используются. Обычно они просто используются для заполнения списков параметров.
  • Поддельные объекты имеют рабочие реализации, но, как правило, используют некоторые сокращения, которые делают их непригодными для производства (хорошим примером является база данных в памяти).
  • Заглушки предоставляют готовые ответы на звонки, сделанные во время теста, обычно не отвечая ни на что, кроме того, что запрограммировано для теста. Заглушки также могут записывать информацию о звонках, например заглушка шлюза электронной почты, которая запоминает сообщения, которые она «отправила», или, может быть, только то, сколько сообщений она «отправила».
  • Здесь мы говорим о макетах: объектах, предварительно запрограммированных с ожиданиями, которые формируют спецификацию вызовов, которые они ожидают получить.

1.4. Издеваться или не издеваться: вот в чем вопрос

Не надо все высмеивать . Иногда лучше провести интеграционный тест, так как издевательство над этим методом/функцией принесет лишь небольшую реальную пользу. В нашем тестовом случае (который будет показан в следующем пункте) это будет тестирование LoginDao .

LoginDao будет использовать какую-то стороннюю библиотеку для доступа к БД, и ее мока будет заключаться только в том, чтобы убедиться, что параметры были подготовлены для вызова, но нам все равно нужно будет проверить, что вызов возвращает нужные нам данные.

По этой причине он не будет включен в этот пример (хотя мы могли бы написать как модульный тест с фиктивными вызовами для вызовов сторонних библиотек, так и интеграционный тест с DBUnit для проверки фактической производительности сторонней библиотеки).

2. Тестовый случай

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

2.1. Предлагаемый случай

Предлагаемый тестовый пример будет процессом входа в приложение с многоуровневой архитектурой.

Запрос на вход будет обрабатываться контроллером, который использует службу, которая использует DAO (который ищет учетные данные пользователя в БД). Мы не будем слишком углубляться в реализацию каждого уровня и сосредоточимся больше на взаимодействии между компонентами каждого уровня.

Таким образом, у нас будут LoginController , LoginService и LoginDAO . Давайте посмотрим на схему для пояснения:

./fb1e3c293191cddb2d809966ec3bf829.png

2.2. Реализация

Теперь мы рассмотрим реализацию, используемую для тестового случая, чтобы мы могли понять, что происходит (или что должно происходить) в тестах.

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

public class UserForm {
public String password;
public String username;
public String getUsername(){
return username;
}
}

Давайте продолжим с LoginDAO , который будет лишен функциональности, поскольку мы хотим, чтобы его методы были там, чтобы мы могли издеваться над ними, когда это необходимо:

public class LoginDao {
public int login(UserForm userForm){
return 0;
}
}

LoginDao будет использоваться LoginService в методе входа в систему . LoginService также будет иметь метод setCurrentUser , который возвращает void для проверки этого издевательства.

public class LoginService {
private LoginDao loginDao;
private String currentUser;

public boolean login(UserForm userForm) {
assert null != userForm;
int loginResults = loginDao.login(userForm);
switch (loginResults){
case 1:
return true;
default:
return false;
}
}

public void setCurrentUser(String username) {
if(null != username){
this.currentUser = username;
}
}
}

Наконец, LoginController будет использовать LoginService в качестве метода входа в систему . Это будет включать:

  • случай, в котором вызовы имитируемой службы не выполняются.
  • случай, когда будет вызван только один метод.
  • случай, в котором будут вызываться все методы.
  • случай, в котором будет проверено генерирование исключений.
public class LoginController {
public LoginService loginService;

public String login(UserForm userForm){
if(null == userForm){
return "ERROR";
}else{
boolean logged;

try {
logged = loginService.login(userForm);
} catch (Exception e) {
return "ERROR";
}

if(logged){
loginService.setCurrentUser(userForm.getUsername());
return "OK";
}else{
return "KO";
}
}
}
}

Теперь, когда мы увидели, что мы пытаемся протестировать, давайте посмотрим, как мы будем имитировать это с каждой библиотекой.

3. Тестовая установка

3.1. Мокито

Для Mockito мы будем использовать версию 2.8.9 .

Самый простой способ создания и использования макетов — с помощью аннотаций @Mock и @InjectMocks . Первый создаст макет для класса, используемого для определения поля, а второй попытается внедрить указанные созданные макеты в аннотированный макет.

Есть и другие аннотации, такие как @Spy , которые позволяют вам создать частичный макет (макет, который использует обычную реализацию в методах без макета).

При этом вам нужно вызвать MockitoAnnotations.initMocks(this) перед выполнением любых тестов, которые будут использовать указанные моки для работы всей этой «магии». Обычно это делается в аннотированном методе @Before . Вы также можете использовать MockitoJUnitRunner .

public class LoginControllerTest {

@Mock
private LoginDao loginDao;

@Spy
@InjectMocks
private LoginService spiedLoginService;

@Mock
private LoginService loginService;

@InjectMocks
private LoginController loginController;

@Before
public void setUp() {
loginController = new LoginController();
MockitoAnnotations.initMocks(this);
}
}

3.2. EasyMock

Для EasyMock мы будем использовать версию 3.4 ( Javadoc ). Обратите внимание, что с EasyMock, чтобы макеты начали «работать», вы должны вызывать EasyMock.replay(mock) для каждого метода тестирования, иначе вы получите исключение.

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

Моки создаются с аннотацией @Mock , а тестируемый объект — с аннотацией @TestSubject (который будет получать свои зависимости от созданных моков). Тестируемый объект должен быть создан в режиме реального времени.

@RunWith(EasyMockRunner.class)
public class LoginControllerTest {

@Mock
private LoginDao loginDao;

@Mock
private LoginService loginService;

@TestSubject
private LoginController loginController = new LoginController();
}

3.3. JMockit

Для JMockit мы будем использовать версию 1.24 ( Javadoc ), поскольку версия 1.25 еще не выпущена (по крайней мере, на момент написания этой статьи).

Настройка для JMockit так же проста, как и для Mockito, за исключением того, что нет специальной аннотации для частичных макетов (да и в этом нет необходимости), и что вы должны использовать JMockit в качестве средства запуска тестов.

Макеты определяются с помощью аннотации @Injectable (которая создаст только один экземпляр макета) или с помощью аннотации @Mocked (которая создаст макеты для каждого экземпляра класса аннотированного поля).

Тестируемый экземпляр создается (и внедряются его фиктивные зависимости) с использованием аннотации @Tested .

@RunWith(JMockit.class)
public class LoginControllerTest {

@Injectable
private LoginDao loginDao;

@Injectable
private LoginService loginService;

@Tested
private LoginController loginController;
}

4. Проверка отсутствия вызовов для имитации

4.1. Мокито

Для проверки того, что макет не получал вызовов в Mockito, у вас есть метод verifyNoInteractions() , который принимает макет.

@Test
public void assertThatNoMethodHasBeenCalled() {
loginController.login(null);
Mockito.verifyNoInteractions(loginService);
}

4.2. EasyMock

Для проверки того, что макет не получил никаких вызовов, вы просто не указываете поведение, вы воспроизводите макет и, наконец, проверяете его.

@Test
public void assertThatNoMethodHasBeenCalled() {
EasyMock.replay(loginService);
loginController.login(null);
EasyMock.verify(loginService);
}

4.3. JMockit

Для проверки того, что макет не получал вызовов, вы просто не указываете ожидания для этого макета и выполняете FullVerifications (макет) для указанного макета.

@Test
public void assertThatNoMethodHasBeenCalled() {
loginController.login(null);
new FullVerifications(loginService) {};
}

5. Определение фиктивных вызовов методов и проверка вызовов фиктивных методов

5.1. Мокито

Для имитационных вызовов методов вы можете использовать Mockito.when(mock.method(args)).thenReturn(value) . Здесь вы можете возвращать разные значения для нескольких вызовов, просто добавляя их в качестве дополнительных параметров: thenReturn(value1, value2, value-n, …) .

Обратите внимание, что с помощью этого синтаксиса вы не можете имитировать методы, возвращающие void. В указанных случаях вы будете использовать проверку указанного метода (как показано в строке 11).

Для проверки вызовов макета вы можете использовать Mockito.verify(mock).method(args) и вы также можете проверить, что больше не было вызовов макета, используя verifyNoMoreInteractions(mock) .

Для проверки args вы можете передавать определенные значения или использовать предопределенные сопоставители, такие как any() , anyString() , anyInt() . Таких сопоставителей гораздо больше, и даже есть возможность определить свои сопоставители, которые мы увидим в следующих примерах.

@Test
public void assertTwoMethodsHaveBeenCalled() {
UserForm userForm = new UserForm();
userForm.username = "foo";
Mockito.when(loginService.login(userForm)).thenReturn(true);

String login = loginController.login(userForm);

Assert.assertEquals("OK", login);
Mockito.verify(loginService).login(userForm);
Mockito.verify(loginService).setCurrentUser("foo");
}

@Test
public void assertOnlyOneMethodHasBeenCalled() {
UserForm userForm = new UserForm();
userForm.username = "foo";
Mockito.when(loginService.login(userForm)).thenReturn(false);

String login = loginController.login(userForm);

Assert.assertEquals("KO", login);
Mockito.verify(loginService).login(userForm);
Mockito.verifyNoMoreInteractions(loginService);
}

5.2. EasyMock

Для фиктивных вызовов метода вы используете EasyMock.expect(mock.method(args)).andReturn(value) .

Для проверки вызовов макета вы можете использовать EasyMock .verify(mock) , но вы должны вызывать его всегда после вызова EasyMock.replay(mock) .

Для проверки args вы можете передавать определенные значения или у вас есть предопределенные сопоставители, такие как isA (Class.class) , anyString() , anyInt() и многие другие сопоставители такого типа, а также возможность определить свои сопоставители.

@Test
public void assertTwoMethodsHaveBeenCalled() {
UserForm userForm = new UserForm();
userForm.username = "foo";
EasyMock.expect(loginService.login(userForm)).andReturn(true);
loginService.setCurrentUser("foo");
EasyMock.replay(loginService);

String login = loginController.login(userForm);

Assert.assertEquals("OK", login);
EasyMock.verify(loginService);
}

@Test
public void assertOnlyOneMethodHasBeenCalled() {
UserForm userForm = new UserForm();
userForm.username = "foo";
EasyMock.expect(loginService.login(userForm)).andReturn(false);
EasyMock.replay(loginService);

String login = loginController.login(userForm);

Assert.assertEquals("KO", login);
EasyMock.verify(loginService);
}

5.3. JMockit

С JMockit вы определили шаги для тестирования: запись, воспроизведение и проверка.

Запись выполняется в новом блоке Expectations(){{}} (в котором вы можете определить действия для нескольких моков), воспроизведение выполняется простым вызовом метода тестируемого класса (который должен вызывать какой-то имитируемый объект), а проверка выполняется делается внутри нового блока Verifications(){{}} (в котором вы можете определить проверки для нескольких макетов).

Для фиктивных вызовов метода вы можете использовать mock.method(args); результат = значение; внутри любого блока ожиданий . Здесь вы можете вернуть разные значения для более чем одного вызова, просто используя return(value1, value2, …, valuen); вместо результата = значение; .

Для проверки вызовов макета вы можете использовать новые проверки (){{mock.call(value)}} или новые проверки (макет){{}} для проверки каждого ранее определенного ожидаемого вызова.

Для проверки args вы можете передать определенные значения или у вас есть предопределенные значения , такие как any , anyString , anyLong и многие другие специальные значения такого рода, а также возможность определить ваши сопоставители (это должны быть сопоставители Hamcrest).

@Test
public void assertTwoMethodsHaveBeenCalled() {
UserForm userForm = new UserForm();
userForm.username = "foo";
new Expectations() {{
loginService.login(userForm); result = true;
loginService.setCurrentUser("foo");
}};

String login = loginController.login(userForm);

Assert.assertEquals("OK", login);
new FullVerifications(loginService) {};
}

@Test
public void assertOnlyOneMethodHasBeenCalled() {
UserForm userForm = new UserForm();
userForm.username = "foo";
new Expectations() {{
loginService.login(userForm); result = false;
// no expectation for setCurrentUser
}};

String login = loginController.login(userForm);

Assert.assertEquals("KO", login);
new FullVerifications(loginService) {};
}

6. Имитация генерации исключений

6.1. Мокито

Генерацию исключений можно имитировать с помощью .thenThrow(ExceptionClass.class) после Mockito.when(mock.method(args)) .

@Test
public void mockExceptionThrowing() {
UserForm userForm = new UserForm();
Mockito.when(loginService.login(userForm)).thenThrow(IllegalArgumentException.class);

String login = loginController.login(userForm);

Assert.assertEquals("ERROR", login);
Mockito.verify(loginService).login(userForm);
Mockito.verifyNoInteractions(loginService);
}

6.2. EasyMock

Генерацию исключений можно имитировать с помощью .andThrow(new ExceptionClass()) после вызова EasyMock.expect(…) .

@Test
public void mockExceptionThrowing() {
UserForm userForm = new UserForm();
EasyMock.expect(loginService.login(userForm)).andThrow(new IllegalArgumentException());
EasyMock.replay(loginService);

String login = loginController.login(userForm);

Assert.assertEquals("ERROR", login);
EasyMock.verify(loginService);
}

6.3. JMockit

Имитация генерации исключений с помощью JMockito особенно проста. Просто верните Exception в результате фиктивного вызова метода вместо «нормального» возврата.

@Test
public void mockExceptionThrowing() {
UserForm userForm = new UserForm();
new Expectations() {{
loginService.login(userForm); result = new IllegalArgumentException();
// no expectation for setCurrentUser
}};

String login = loginController.login(userForm);

Assert.assertEquals("ERROR", login);
new FullVerifications(loginService) {};
}

7. Насмешка над объектом для передачи

7.1. Мокито

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

@Test
public void mockAnObjectToPassAround() {
UserForm userForm = Mockito.when(Mockito.mock(UserForm.class).getUsername())
.thenReturn("foo").getMock();
Mockito.when(loginService.login(userForm)).thenReturn(true);

String login = loginController.login(userForm);

Assert.assertEquals("OK", login);
Mockito.verify(loginService).login(userForm);
Mockito.verify(loginService).setCurrentUser("foo");
}

7.2. EasyMock

Макеты могут быть созданы с помощью EasyMock.mock(Class.class) . После этого вы можете использовать EasyMock.expect(mock.method()) , чтобы подготовить его к выполнению, всегда не забывая вызывать EasyMock.replay(mock) перед его использованием.

@Test
public void mockAnObjectToPassAround() {
UserForm userForm = EasyMock.mock(UserForm.class);
EasyMock.expect(userForm.getUsername()).andReturn("foo");
EasyMock.expect(loginService.login(userForm)).andReturn(true);
loginService.setCurrentUser("foo");
EasyMock.replay(userForm);
EasyMock.replay(loginService);

String login = loginController.login(userForm);

Assert.assertEquals("OK", login);
EasyMock.verify(userForm);
EasyMock.verify(loginService);
}

7.3. JMockit

Чтобы имитировать объект только для одного метода, вы можете просто передать его в качестве параметра тестовому методу. Затем вы можете создать ожидания, как и в случае с любым другим макетом.

@Test
public void mockAnObjectToPassAround(@Mocked UserForm userForm) {
new Expectations() {{
userForm.getUsername(); result = "foo";
loginService.login(userForm); result = true;
loginService.setCurrentUser("foo");
}};

String login = loginController.login(userForm);

Assert.assertEquals("OK", login);
new FullVerifications(loginService) {};
new FullVerifications(userForm) {};
}

8. Пользовательское сопоставление аргументов

8.1. Мокито

Иногда сопоставление аргументов для имитируемых вызовов должно быть немного сложнее, чем просто фиксированное значение или anyString() . Для этих случаев у Mockito есть свой класс сопоставления, который используется с argThat(ArgumentMatcher<>) .

@Test
public void argumentMatching() {
UserForm userForm = new UserForm();
userForm.username = "foo";
// default matcher
Mockito.when(loginService.login(Mockito.any(UserForm.class))).thenReturn(true);

String login = loginController.login(userForm);

Assert.assertEquals("OK", login);
Mockito.verify(loginService).login(userForm);
// complex matcher
Mockito.verify(loginService).setCurrentUser(ArgumentMatchers.argThat(
new ArgumentMatcher<String>() {
@Override
public boolean matches(String argument) {
return argument.startsWith("foo");
}
}
));
}

8.2. EasyMock

Сопоставление пользовательских аргументов немного сложнее с EasyMock, так как вам нужно создать статический метод, в котором вы создаете фактический сопоставитель, а затем сообщаете об этом с помощью EasyMock.reportMatcher(IArgumentMatcher) .

Как только этот метод создан, вы используете его в своих ложных ожиданиях с вызовом метода (как показано в примере в строке).

@Test
public void argumentMatching() {
UserForm userForm = new UserForm();
userForm.username = "foo";
// default matcher
EasyMock.expect(loginService.login(EasyMock.isA(UserForm.class))).andReturn(true);
// complex matcher
loginService.setCurrentUser(specificArgumentMatching("foo"));
EasyMock.replay(loginService);

String login = loginController.login(userForm);

Assert.assertEquals("OK", login);
EasyMock.verify(loginService);
}

private static String specificArgumentMatching(String expected) {
EasyMock.reportMatcher(new IArgumentMatcher() {
@Override
public boolean matches(Object argument) {
return argument instanceof String
&& ((String) argument).startsWith(expected);
}

@Override
public void appendTo(StringBuffer buffer) {
//NOOP
}
});
return null;
}

8.3. JMockit

Пользовательское сопоставление аргументов с JMockit выполняется с помощью специального метода withArgThat (Matcher) (который получает объекты Hamcrest Matcher ).

@Test
public void argumentMatching() {
UserForm userForm = new UserForm();
userForm.username = "foo";
// default matcher
new Expectations() {{
loginService.login((UserForm) any);
result = true;
// complex matcher
loginService.setCurrentUser(withArgThat(new BaseMatcher<String>() {
@Override
public boolean matches(Object item) {
return item instanceof String && ((String) item).startsWith("foo");
}

@Override
public void describeTo(Description description) {
//NOOP
}
}));
}};

String login = loginController.login(userForm);

Assert.assertEquals("OK", login);
new FullVerifications(loginService) {};
}

9. Частичная насмешка

9.1. Мокито

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

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

@Test
public void partialMocking() {
// use partial mock
loginController.loginService = spiedLoginService;
UserForm userForm = new UserForm();
userForm.username = "foo";
// let service's login use implementation so let's mock DAO call
Mockito.when(loginDao.login(userForm)).thenReturn(1);

String login = loginController.login(userForm);

Assert.assertEquals("OK", login);
// verify mocked call
Mockito.verify(spiedLoginService).setCurrentUser("foo");
}

9.2. EasyMock

Частичная имитация также становится немного сложнее с EasyMock, так как вам нужно определить, какие методы будут имитироваться при создании макета.

Это делается с помощью EasyMock.partialMockBuilder(Class.class).addMockedMethod("methodName").createMock() . Как только это будет сделано, вы можете использовать макет как любой другой нечастичный макет.

@Test
public void partialMocking() {
UserForm userForm = new UserForm();
userForm.username = "foo";
// use partial mock
LoginService loginServicePartial = EasyMock.partialMockBuilder(LoginService.class)
.addMockedMethod("setCurrentUser").createMock();
loginServicePartial.setCurrentUser("foo");
// let service's login use implementation so let's mock DAO call
EasyMock.expect(loginDao.login(userForm)).andReturn(1);

loginServicePartial.setLoginDao(loginDao);
loginController.loginService = loginServicePartial;

EasyMock.replay(loginDao);
EasyMock.replay(loginServicePartial);

String login = loginController.login(userForm);

Assert.assertEquals("OK", login);
// verify mocked call
EasyMock.verify(loginServicePartial);
EasyMock.verify(loginDao);
}

9.3. JMockit

Частичное издевательство с JMockit особенно просто. Каждый вызов метода, для которого в Expectations(){{}} не было определено фиктивное поведение, использует «настоящую» реализацию .

Теперь давайте представим, что мы хотим частично имитировать класс LoginService , чтобы имитировать метод setCurrentUser() при использовании фактической реализации метода login() .

Для этого мы сначала создаем и передаем экземпляр LoginService в блок ожиданий. Затем мы записываем только ожидание для метода setCurrentUser() :

@Test
public void partialMocking() {
LoginService partialLoginService = new LoginService();
partialLoginService.setLoginDao(loginDao);
loginController.loginService = partialLoginService;

UserForm userForm = new UserForm();
userForm.username = "foo";

new Expectations(partialLoginService) {{
// let's mock DAO call
loginDao.login(userForm); result = 1;

// no expectation for login method so that real implementation is used

// mock setCurrentUser call
partialLoginService.setCurrentUser("foo");
}};

String login = loginController.login(userForm);

Assert.assertEquals("OK", login);
// verify mocked call
new Verifications() {{
partialLoginService.setCurrentUser("foo");
}};
}

10. Заключение

В этом посте мы сравнили три библиотеки макетов Java, каждая со своими сильными сторонами и недостатками.

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

  • Мы бы сказали, что Mockito выиграет здесь, поскольку у него есть специальная аннотация для частичных макетов, но JMockit даже не нуждается в этом, поэтому скажем, что это ничья между этими двумя.

  • All three of them follow more or less the record-replay-verify pattern , but in our opinion, the best one to do so is JMockit as it forces you to use those in blocks, so tests get more structured.

  • Easiness of use is important so you can work as less as possible to define your tests. JMockit will be the chosen option for its fixed-always-the-same structure.

  • Mockito is more or less THE most known so that the community will be bigger.

  • Having to call replay every time you want to use a mock is a clear no-go , so we'll put a minus one for EasyMock.

  • Consistency/simplicity is also important for me. We loved the way of returning results of JMockit that is the same for “normal” results as for exceptions.

Will all this being said, we're going to choose JMockit as a kind of a winner even though up till now we've been using Mockito as we've been captivated by its simplicity and fixed structure and will try and use it from now on.

The full implementation of this tutorial can be found on the GitHub project so feel free to download it and play with it.