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

JMockit 101

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

1. Введение

Этой статьей мы начнем новую серию, посвященную набору инструментов для имитации JMockit .

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

В последующих статьях мы сосредоточимся на его возможностях и углубимся в них.

2. ДжМокит

2.1. Введение

Прежде всего, поговорим о том, что такое JMockit: Java-фреймворк для мокирования объектов в тестах (его можно использовать как для JUnit , так и для TestNG ).

Он использует инструментальные API-интерфейсы Java для изменения байт-кода классов во время выполнения, чтобы динамически изменять их поведение. Некоторыми из его сильных сторон являются его выразимость и нестандартная способность имитировать статические и частные методы.

Возможно, вы новичок в JMockit, но это определенно не из-за того, что он новый. Разработка JMockit началась в июне 2006 года, а его первый стабильный выпуск датируется декабрем 2012 года, так что он существует уже некоторое время (на момент написания статьи текущая версия — 1.24).

2.2. Зависимость от Maven

Во-первых, нам нужно добавить зависимость jmockit в наш проект:

<dependency> 
<groupId>org.jmockit</groupId>
<artifactId>jmockit</artifactId>
<version>1.41</version>
</dependency>

2.3. Выразимость JMockit

Как было сказано ранее, одной из самых сильных сторон JMockit является его выразительность. Чтобы создавать моки и определять их поведение, вместо вызова методов из API-мокинга вам просто нужно определить их напрямую.

Это означает, что вы не будете делать такие вещи, как:

API.expect(mockInstance.method()).andThenReturn(value).times(2);

Вместо этого ожидайте таких вещей, как:

new Expectation() {
mockInstance.method();
result = value;
times = 2;
}

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

Если принять во внимание, что в части result = value вы можете вернуть что угодно (фиксированные значения, динамически генерируемые значения, исключения и т. д.), выразительность JMockit становится еще более очевидной.

2.4. Модель «запись-воспроизведение-проверка»

Тесты с использованием JMockit делятся на три дифференцированных этапа: запись, воспроизведение и проверка.

  1. На этапе записи , во время подготовки к тесту и перед вызовами методов, которые мы хотим выполнить, мы определим ожидаемое поведение для всех тестов, которые будут использоваться на следующем этапе.
  2. Фаза воспроизведения — это фаза, на которой выполняется тестируемый код. Вызовы фиктивных методов/конструкторов, ранее записанные на предыдущем этапе, теперь будут воспроизводиться.
  3. Наконец, на этапе проверки мы утверждаем, что результат теста был таким, как мы ожидали (и что моки вели себя и использовались в соответствии с тем, что было определено на этапе записи).

На примере кода каркас для теста будет выглядеть примерно так:

@Test
public void testWireframe() {
// preparation code not specific to JMockit, if any

new Expectations() {{
// define expected behaviour for mocks
}};

// execute code-under-test

new Verifications() {{
// verify mocks
}};

// assertions
}

3. Создание макетов

3.1. Аннотации JMockit

При использовании JMockit самый простой способ использовать макеты — использовать аннотации. Есть три для создания макетов ( @Mocked , @Injectable и @Capturing ) и один для указания тестируемого класса ( @Tested ).

При использовании аннотации @Mocked для поля будут создаваться фиктивные экземпляры каждого нового объекта этого конкретного класса.

С другой стороны, с аннотацией @Injectable будет создан только один фиктивный экземпляр.

Последняя аннотация, @Capturing , будет вести себя как @Mocked, но будет распространяться на каждый подкласс, расширяющий или реализующий тип аннотированного поля.

3.2. Передача аргументов в тесты

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

@RunWith(JMockit.class)
public class TestPassingArguments {

@Injectable
private Foo mockForEveryTest;

@Tested
private Bar bar;

@Test
public void testExample(@Mocked Xyz mockForJustThisTest) {
new Expectations() {{
mockForEveryTest.someMethod("foo");
mockForJustThisTest.someOtherMethod();
}};

bar.codeUnderTest();
}
}

Этот способ создания макета путем передачи его в качестве параметра вместо вызова какого-либо метода API снова показывает нам выразительность, о которой мы говорили с самого начала.

3.3. Полный пример

В завершение этой статьи мы добавим полный пример теста с использованием JMockit.

В этом примере мы будем тестировать класс Performer , который использует Collaborator в своем методе Perform() . Этот метод Perform ( ) получает объект Model в качестве параметра, из которого он будет использовать свой метод getInfo () , который возвращает строку, эта строка будет передана в метод Collaborator () из Collaborator , который вернет true для этого конкретного теста, и это значение будет передано методу receive() из Collaborator .

Итак, тестируемые классы будут выглядеть так:

public class Model {
public String getInfo(){
return "info";
}
}

public class Collaborator {
public boolean collaborate(String string){
return false;
}
public void receive(boolean bool){
// NOOP
}
}

public class Performer {
private Collaborator collaborator;

public void perform(Model model) {
boolean value = collaborator.collaborate(model.getInfo());
collaborator.receive(value);
}
}

И код теста будет выглядеть так:

@RunWith(JMockit.class)
public class PerformerTest {

@Injectable
private Collaborator collaborator;

@Tested
private Performer performer;

@Test
public void testThePerformMethod(@Mocked Model model) {
new Expectations() {{
model.getInfo();result = "bar";
collaborator.collaborate("bar"); result = true;
}};
performer.perform(model);
new Verifications() {{
collaborator.receive(true);
}};
}
}

4. Вывод

На этом мы завершим наше практическое знакомство с JMockit. Если вы хотите узнать больше о JMockit, следите за будущими статьями.

Полную реализацию этого руководства можно найти в проекте GitHub.

4.1. Статьи в серии

Все статьи цикла: