Статьи

Ложные статические методы с использованием Spring-аспектов

Я являюсь пользователем Spring Framework за последние три года, и мне очень понравилось работать с Spring. Одна вещь, которую я вижу в эти дни, — интенсивное использование AspectJ в большинстве продуктов SpringSource. Spring Roo — это RAD-инструмент для Java-разработчиков, который использует AspectJ ITD для отдельных модулей компиляции. Управление транзакциями Spring и трансляция исключений также выполняются с использованием Aspectj. Существует также множество других способов использования продуктов Aspectj in Spring. В этой статье я расскажу о другом классном использовании AspectJ в Spring — Mocking Static Methods.

В наши дни большинство разработчиков пишут примеры модульных тестов, и очень часто используют какие-либо библиотеки-насмешки, такие как EasyMock , Mockito и т. Д. , Для имитации внешних зависимостей класса. Используя любую из этих библиотек для имитации, очень легко смоделировать вызовы другого метода экземпляра класса. Но большинство из этих фальшивых фреймворков не позволяют имитировать вызовы статических методов. Spring предоставляет возможность имитировать статические методы с помощью библиотеки Spring Aspect. Чтобы использовать эту функцию, вам нужно добавить в ваш pom.xml зависимость spring-aspect.jar.

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>3.0.4.RELEASE</version>
</dependency>

 

Следующее, что вам нужно сделать, это преобразовать ваш проект в проект AspectJ. Если вы используете Eclipse или STS (SpringSource Tool Suite), вы можете сделать это, щелкнув правой кнопкой мыши по вашему проекту -> configure -> convert to AspectJ project. STS по умолчанию имеет плагин AspectJ, для пользователей Eclipse вам необходимо установить плагин AspectJ для работы выше. Я бы рекомендовал использовать STS для разработки приложений на базе Spring.

Два аспекта и одна аннотация, представляющая интерес для spring-aspect.jar:

  1. AbstractMethodMockingControl : это абстрактный аспект, позволяющий включать моделирование методов, выбранных pointcut. Все дочерние аспекты должны определять точки нажатия mockStaticsTestMethod () и methodToMock (). Pointcut mockStaticTestMethod () используется, чтобы указать, когда должно быть запущено mocking, а pointcut methodToMock () используется, чтобы определить, какие вызовы методов для mock.
  2. AnnotationDrivenStaticEntityMockingControl : это единственная реализация аспекта AbstractMethodMockingControl, которая существует в spring-aspect.jar. Это основанный на аннотациях аспект, который нужно использовать в тестовой сборке, чтобы включить макетирование статических методов в классах сущностей. В этом аспекте mockStaticTestMethod () pointcut определяет, что для классов, помеченных @MockStaticEntityMethods, должно быть инициировано mocking аннотации, а pointTetToMock () определяет, что все общедоступные статические методы в классах, помеченных аннотацией @Entity, должны быть смоделированы.
  3. MockStaticEntityMethods : аннотация для указания тестового класса, для которого методы @Test статические методы классов Entity должны быть смоделированы.

AnnotationDrivenStaticEntityMockingControl предоставляет возможность имитации статических методов любого класса, помеченного аннотацией @Entity. Но обычно нам нужно макетировать статический метод классов, отличных от помеченных аннотацией @Entity. Единственное, что нам нужно сделать, чтобы заставить его работать, это расширить аспект AbstractMethodMockingControl и предоставить определения для точек-меток mockStaticsTestMethod () и methodToMock (). Например, давайте напишем аспект, который должен высмеивать все открытые статические методы классов, помеченных аннотацией @Component .

package com.shekhar.javalobby;

import org.springframework.mock.staticmock.AbstractMethodMockingControl;
import org.springframework.stereotype.Component;
import org.springframework.mock.staticmock.MockStaticEntityMethods;;

public aspect AnnotationDrivenStaticComponentMockingControl extends
AbstractMethodMockingControl {

public static void playback() {
AnnotationDrivenStaticComponentMockingControl.aspectOf().playbackInternal();
}

public static void expectReturn(Object retVal) {
AnnotationDrivenStaticComponentMockingControl.aspectOf().expectReturnInternal(retVal);
}

public static void expectThrow(Throwable throwable) {
AnnotationDrivenStaticComponentMockingControl.aspectOf().expectThrowInternal(throwable);
}

protected pointcut mockStaticsTestMethod() : execution(public * (@MockStaticEntityMethods *).*(..));

protected pointcut methodToMock() : execution(public static * (@Component *).*(..));
}

Единственное различие между AnnotationDrivenStaticEntityMockingControl (поставляется с spring-aspect.jar) и AnnotationDrivenStaticComponentMockingControl (пользовательский, который мы написали выше) заключается в методе pointcut метода ToMock (). В методе pointToMock () pointcut мы указали, что он должен макетировать все статические методы в любом классе, помеченном аннотацией @Component.

Теперь, когда мы написали пользовательский аспект, давайте проверим его. Я создал простой ExampleService с одним статическим методом. Это метод, который мы хотим высмеять.

@Component
public class ExampleService implements Service {

/**
* Reads next record from input
*/
public String getMessage() {
return myName();
}

public static String myName() {
return "shekhar";
}

}

Этот класс вернет «shekhar», когда будет вызван метод getMessage (). Давайте проверим это без насмешек

package com.shekhar.javalobby;

import org.junit.Assert;
import org.junit.Test;

public class ExampleConfigurationTests {

private ExampleService service = new ExampleService();

@Test
public void testSimpleProperties() throws Exception {
String myName = service.getMessage();
Assert.assertEquals("shekhar", myName);
}

}

 

Этот тест будет работать нормально. Теперь давайте добавим насмешку к этому тестовому классу. Есть две вещи, которые мы должны сделать в нашем тесте

  1. Нам нужно аннотировать наш тест с помощью @MockStaticEntityMethods, чтобы указать, что статические методы классов @Component будут подвергнуты насмешке. Обратите внимание, что использовать аннотацию @MockStaticEntityMethods необязательно, вы можете создать свою собственную аннотацию и использовать ее в pointc mockStaticsTestMethod (). Итак, я мог бы создать аннотацию под названием @MockStaticComponentMethods и использовать ее в pointc mockStaticsTestMethod (). Но я только что использовал аннотацию @MockStaticEntityMethods.
  2. В наших тестовых методах нам нужно сначала вызвать статический метод, который мы хотим смоделировать, чтобы он был записан. Затем нам нужно установить наше ожидание, то есть то, что должно быть возвращено из макета, и, наконец, нам нужно вызвать метод воспроизведения, чтобы остановить запись фиктивных вызовов и войти в состояние воспроизведения.

Чтобы сделать его более конкретным, давайте применим насмешку к вышеупомянутому тесту.

import org.junit.Assert;
import org.junit.Test;
import org.springframework.mock.staticmock.MockStaticEntityMethods;

@MockStaticEntityMethods
public class ExampleConfigurationTests {

private ExampleService service = new ExampleService();

@Test
public void testSimpleProperties() throws Exception {
ExampleService.myName();
AnnotationDrivenStaticComponentMockingControl.expectReturn("shekhargulati");
AnnotationDrivenStaticComponentMockingControl.playback();
String myName = service.getMessage();
Assert.assertEquals("shekhargulati", myName);
}

}

 

Как видите, мы аннотировали тестовый класс аннотацией @MockStaticEntityMethods, а в методе теста мы сначала записали вызов (ExampleService.myName ()), затем установили ожидания, затем выполнили воспроизведение и, наконец, вызвали реальный код.

Таким образом, вы можете издеваться над статическим методом класса.