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 делятся на три дифференцированных этапа: запись, воспроизведение и проверка.
- На этапе записи , во время подготовки к тесту и перед вызовами методов, которые мы хотим выполнить, мы определим ожидаемое поведение для всех тестов, которые будут использоваться на следующем этапе.
- Фаза воспроизведения — это фаза, на которой выполняется тестируемый код. Вызовы фиктивных методов/конструкторов, ранее записанные на предыдущем этапе, теперь будут воспроизводиться.
- Наконец, на этапе проверки мы утверждаем, что результат теста был таким, как мы ожидали (и что моки вели себя и использовались в соответствии с тем, что было определено на этапе записи).
На примере кода каркас для теста будет выглядеть примерно так:
@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. Статьи в серии
Все статьи цикла: