JMockit 101

JMockit 101

1. Вступление

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

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

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

2. JMockit

2.1. Вступление

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

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

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

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

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

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

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

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

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

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

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

2.3. Модель записи-воспроизведения-проверки

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

  1. На этапеrecord, во время подготовки теста и перед вызовами методов, которые мы хотим выполнить, мы определим ожидаемое поведение для всех тестов, которые будут использоваться на следующем этапе.

  2. Фазаreplay - это та, на которой выполняется тестируемый код. Вызовы надуманных методов / конструкторов, ранее записанных на предыдущем этапе, теперь будут воспроизведены.

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

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

@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 самый простой способ использовать mocks - это использовать аннотации. Их три для создания макетов (@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(), который возвращает строку, эта строка будет передана методуcollaborate() из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, следите за будущими статьями.

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