Статьи

Интеграционное тестирование объединенных компонентов в CDI 1.0 и Spring 3.1

В этом посте я опишу, как проводить интеграционное тестирование с bean-компонентами в Spring и CDI. Все проиллюстрировано небольшими примерами кода. Интеграционное тестирование с областями применения не особенно легко. Представьте бин, который живет в области действия сеанса, например UserCredentials . В интеграционном тесте у вас обычно нет HttpRequest или HttpSession для работы (по крайней мере, если вы не выполняете тесты, включающие ваш пользовательский интерфейс). Поэтому вам нужна инфраструктура для тестирования интеграции. С обеими технологиями это немного озадачивает, чтобы эта инфраструктура заработала. Получите свою собственную картину этого.

Если вы новичок в области видимости и контексте в CDI и Spring, ознакомьтесь с основами и получите обзор различных областей применения.

Интеграционное тестирование объединенных бобов в Spring

В Spring 3.1 отсутствует поддержка интеграционных тестов для сессионных компонентов или компонентов запроса (см. Здесь ). Это запланировано на весну версии 3.2. Тем не менее, эта ссылка объясняет решение, которое работало для меня.

Сначала вам нужно разработать SessionScope для теста. Его целью является макет HttpRequest и HttpSession.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
package com.mycompany.springapp.scope;
 
import org.springframework.beans.factory.InitializingBean;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpSession;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.context.request.SessionScope;
 
public class SetupSession extends SessionScope implements InitializingBean {
 
 public void afterPropertiesSet() throws Exception {
  MockHttpServletRequest request = new MockHttpServletRequest();
  MockHttpSession session = new MockHttpSession();
  request.setSession(session);
  RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(
    request));
 }
 
}

Чтобы зарегистрировать этот класс в качестве объекта управления областью сеанса в вашем test-beans.xml сделайте следующее:

01
02
03
04
05
06
07
08
09
10
11
12
 

Обратите внимание, что я зарегистрировал области действия после context:component-scan тэг context:component-scan ,

Наконец, я написал свой тестовый класс:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.util.Assert;
 
@ContextConfiguration("/test-beans.xml")
@RunWith(SpringJUnit4ClassRunner.class)
public class MyScopeBeanTest {
  
 @Autowired
 private MyScopeBean myScopeBean;
  
 @Test
 public void testBeanScopes() {
  Assert.isTrue(myScopeBean.getMyCustomScopedService().getName().equals("Test"));
  Assert.isTrue(myScopeBean.getMySessionScopedService().getName().equals("Test"));
 }
 
}

Обратите внимание, что я вызвал метод getName() EJB. Это необходимо для обеспечения работоспособности. Клиентский прокси-сервер может быть внедрен в точку внедрения, но если вы сделаете вызов прокси-серверу, у него не будет ссылки на объект области действия и сотрудничающий объект соответственно.

Интеграционное тестирование объединенных бобов с CDI

Инструмент, который я использовал для тестирования интеграции CDI — Arquillian . Есть альтернативы. Вы можете использовать Weld «изначально», если тестируете только с классами CDI. Но если у вас также есть EJB, этого недостаточно. Arquillian поставляется с разумным количеством переходных зависимостей. Давайте посмотрим, как все будет работать.

Примечание: без Maven вы потерялись здесь в пустыне, поэтому я призываю вас использовать его! Я попробовал m2eclipse для Helios, у меня это не сработало, я вернулся к старой доброй командной строке, используя Maven 3.

Изменения в вашем файле pom.xml

В этих примерах предполагается, что у вас работает проект Java EE, вы также можете посмотреть здесь, как настроить новый проект Java EE 6. Для интеграции Arquillian внесите следующие изменения в ваш файл pom.xml :

В разделе свойств:

1
1.0.0.Alpha5

Добавьте этот репозиторий:

01
02
03
04
05
06
07
08
09
10
11
12
13
repository.jboss.org
default
 
   true
   never
   warn
 
 
   false
   always
   warn

Это официальный репозиторий JBoss Maven, в котором доступны все дистрибутивы Arquillian.

Добавьте следующие зависимости в ваш pom.xml :

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
junit
junit
4.8.1
test
 
 
 
org.jboss.arquillian
arquillian-junit
${arquillian.version}
test
 
 
 
org.jboss.arquillian.container
arquillian-glassfish-remote-3.1
${arquillian.version}
test
 
 
 
javax.enterprise
cdi-api
1.0-SP4
test

Первая зависимость — это ваша среда JUnit для написания интеграционных тестов. Вторая зависимость объединяет Arquillian с JUnit. Третья зависимость объединяет ваш контейнер развертывания. Для меня это моя установка Glassfish. Последняя зависимость — это API-интерфейс CDI, который должен быть доступен для ваших тестов CDI.

Обратите внимание, что в строке 17 я использую установку Glassfish 3.1 в качестве контейнера развертывания, а Arquillian использует удаленные вызовы для выполнения тестов. Вам необходимо настроить собственную среду развертывания здесь. См. JBoss Maven Repo для правильного значения artifactId . С Arquillian вашей целевой средой также может быть встроенный контейнер, такой как JBoss Embedded AS, GlassFish Embedded или Weld SE. В этом случае вам не нужна отдельная установка контейнера и удаленные вызовы, все работает локально («в памяти»).

Вы делаете mvn eclipse: eclipse после добавления зависимостей для вашей целевой среды.

Написание и выполнение теста с Arquillian и JUnit

Наконец я написал свой первый класс тестирования интеграции Arquillian:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
import javax.inject.Inject;
 
import junit.framework.Assert;
 
import org.jboss.arquillian.api.Deployment;
import org.jboss.arquillian.junit.Arquillian;
import org.jboss.shrinkwrap.api.ArchivePaths;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.asset.EmptyAsset;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import org.junit.Test;
import org.junit.runner.RunWith;
 
import com.mycompany.jeeapp.scope.MyApplicationService;
import com.mycompany.jeeapp.scope.MyConversationService;
import com.mycompany.jeeapp.scope.MyDefaultService;
import com.mycompany.jeeapp.scope.MyRequestService;
import com.mycompany.jeeapp.scope.MyScopeBean;
import com.mycompany.jeeapp.scope.MySessionService;
import com.mycompany.jeeapp.scope.MySingletonService;
import com.mycompany.jeeapp.scope.extension.MyCustomScopeService;
 
@RunWith(Arquillian.class)
public class MyArquillianJUnitTest {
 
 @Inject
 private MyScopeBean myScopeBean;
 
 @Deployment
 public static JavaArchive createTestArchive() {
  return ShrinkWrap
    .create(JavaArchive.class, "test.jar")
    .addClasses(MyScopeBean.class,MyApplicationService.class,
      MyConversationService.class, MyDefaultService.class,
      MyRequestService.class, MySessionService.class,
      MySingletonService.class, MyCustomScopeService.class)
    .addAsManifestResource(EmptyAsset.INSTANCE,
      ArchivePaths.create("beans.xml"));
 }
 
 @Test
 public void testScopedBeans() {
  Assert.assertTrue(myScopeBean.getApplicationService().getSomeName()
    .equals("myName"));
  Assert.assertTrue(myScopeBean.getApplicationServiceWithNew().getSomeName()
    .equals("myName"));
  Assert.assertTrue(myScopeBean.getCustomScopeService().getSomeName()
    .equals("myName"));
  Assert.assertTrue(myScopeBean.getDefaultService().getSomeName()
    .equals("myName"));
  Assert.assertTrue(myScopeBean.getRequestService().getSomeName()
    .equals("myName"));
  Assert.assertTrue(myScopeBean.getSessionService().getSomeName()
    .equals("myName"));
  Assert.assertTrue(myScopeBean.getSingletonService().getSomeName()
    .equals("myName"));
 }
 
}

Вывод

В настоящий момент Spring не предлагает интегрированной поддержки тестирования для bean-компонентов с областью видимости. Это было очень удивительно, так как Spring всегда придавал большое значение всем темам тестирования. Существует обходной путь, который я описал в своем блоге. Это было не сложно сделать эту работу. Полная поддержка тестирования интеграции запланирована на Выпуск 3.2 M1.

Тестирование bean-объектов CDI включено с помощью Arquillian. У меня были некоторые проблемы во время установки (см. Последний абзац ниже), которые, как мне кажется, обычны, если вы используете новую технологию. Тот факт, что вы должны передать все тестируемые компоненты в архив (см. Метод @Deployment ) — это то, что мне нужно попробовать в большом проекте: это действительно хорошая идея? Иногда большие приложения соединяются вместе с десятками бинов из разных пакетов. Трудно предсказать, какие компоненты используются в интеграционном тесте.

Проблемы и решения

Некоторые настройки Arquillian имеют так много зависимостей, что вы не можете использовать стандартные конфигурации запуска Eclipse. Сгенерированный аргумент командной строки превышает ограничение длины Windows для команд командной строки. Поэтому я использовал Ant-Script для запуска теста. Сценарий только для иллюстрации. Вы должны создать свой собственный скрипт Ant. Вы можете получить информацию о вашем classpath следующим образом: в Eclipse перейдите к «File> Export> General> Ant buildfiles», чтобы сгенерировать информацию о вашем classpath. Возьмите эту информацию о пути к классам и поместите ее в скрипт запуска теста Ant JUnit. Я задокументировал мой полный скрипт Ant здесь .

Когда я запустил этот Ant-скрипт, у меня все работало нормально. Если у вас есть какие-либо проблемы, дайте мне знать, вы можете просмотреть файл результатов теста и файл server.log для анализа.

Больше сообщений об ошибках во время настройки Arquillian

WELD-001303 Нет активных контекстов для типа области javax.enterprise.context.ConversationScoped

-> ConversationScope связан с JSF спецификацией EE. Таким образом, они не будут активны во время обычного HTTP-запроса, что и делает Аркиллиан.

POST http: // localhost: 4848 / management / domain / apps / application возвратил статус ответа 403

-> 404/403 ошибки могут быть проблемами развертывания, проверьте server.log для основной причины (моя была в том, что у меня не было всех необходимых классов, добавленных в test.jar )

Возникла исключительная ситуация при выполнении командной строки.

Не удалось запустить программу «D: \ dev_home \ java-6-26 \ bin \ javaw.exe» (в каталоге «D: \ dev_home \ repositories \ git \ jee-app-weld \ jee-app-weld»): ошибка CreateProcess = 87, параметр Фальшера

-> Classpath превышает допустимую длину для операций командной строки Windows. Вам нужно использовать скрипт Ant или Maven для запуска тестов.

ValidationException: DeploymentScenario содержит цели, не обрабатывающие какой-либо определенный контейнер в реестре.

-> смотрите здесь .

WELD-000072 Управляемый компонент, объявляющий пассивирующую область, должен поддерживать пассивацию. Бин: управляемый бин [класс com.mycompany.jeeapp.scope.example.UserCredentials] с квалификаторами [@Any @Default]

-> Вам нужно реализовать Serializable на бинах Session- и Conversation-Scoped.

DeploymentScenario содержит цели, не обрабатывающие какой-либо определенный контейнер в реестре. _ДЕФОЛТ_

-> смотрите здесь .

java.net.ConnectException: соединение отказано: подключиться

-> Ваш удаленный сервер Java EE не запущен, запустите его!

Ссылка: «Интеграционное тестирование bean-объектов scoped в CDI 1.0 и Spring 3.1» от нашего партнера JCG Никласа.