Spring: юнит и интеграционные тесты - PullRequest
8 голосов
/ 04 января 2011

Я ищу рекомендации по настройке юнит-тестов и интеграционных тестов с использованием Spring.

Я обычно использую 3 вида тестов:

  • "настоящие" юнит-тесты (нетзависимости)
  • тесты выполняются либо как "модульный" тест (дБ в памяти, локальные вызовы, фиктивные объекты, ...), либо как интеграционный тест (постоянный дБ, удаленные вызовы, ...)
  • тесты выполняются только как интеграционные тесты

В настоящее время у меня есть только тесты второй категории, что является сложной частью.Я настроил базовый тестовый класс, например:

@ContextConfiguration(locations = { "/my_spring_test.xml" })
public abstract class AbstractMyTestCase extends AbstractJUnit4SpringContextTests

И "модульные" тесты, такие как:

public class FooTest extends AbstractMyTestCase

с автоматически связанными атрибутами.

Какой лучший способзапустить тест в другой (интеграционный тест) среде?Подкласс теста и переопределить ContextConfiguration?

@ContextConfiguration(locations = { "/my_spring_integration_test.xml" })
public class FooIntegrationTest extends FooTest

Будет ли это работать (я не могу в настоящее время легко проверить это здесь)?Проблема с этим подходом заключается в том, что "@ContextConfiguration (location = {" /my_spring_integration_test.xml "})" много дублируется.

Есть предложения?

С уважением, Флориан

Ответы [ 4 ]

4 голосов
/ 04 января 2011

Я расширил GenericXmlContextLoader

public class MyContextLoader extends GenericXmlContextLoader {

и перегрузил метод

protected String[] generateDefaultLocations(Class<?> clazz)

, чтобы собратьимена конфигурационных файлов каталога, которые я могу указать с помощью SystemProperty (-Dtest.config =).

Я также изменил метод follwowing, чтобы НЕ изменять какие-либо местоположения

@Override
protected String[] modifyLocations(Class<?> clazz, String... locations) {
    return locations;
}

Я использую этот загрузчик контекста, как этотИсходный код файлов конфигурации позволяет вам теперь использовать совершенно разные конфигурации.

Использование SystemProperty - это, конечно, только одна стратегия для определения местоположения конфигурации.Вы можете делать все, что захотите, в generateDefaultLocations().


РЕДАКТИРОВАТЬ:

Это решение позволяет вам использовать различные конфигурации контекста приложения (например, для фиктивных объектов).) и не только разные свойства.Вам не нужен шаг сборки, чтобы развернуть все в вашем местоположении "classpath".Моя конкретная реализация также использовала имя пользователя по умолчанию для поиска каталога конфигурации (src / test / resources / {user}), если не задано системное свойство (упрощает поддержку определенных сред тестирования для всех разработчиков проекта).

Использование PropertyPlaceholder все еще возможно и рекомендуется.


РЕДАКТИРОВАТЬ :

Версия Spring 3.1.0 будет поддерживать XML-профили / абстракция среды , что аналогично моему решению и позволит выбирать файлы конфигурации для различных сред / профилей.

2 голосов
/ 04 января 2011

Я бы пошел с этой версией:

ContextConfiguration(locations = { "/my_spring_test.xml" })
public abstract class AbstractMyTestCase extends AbstractJUnit4SpringContextTests

и в my_spring_test.xml я бы использовал механизм PropertyPlaceHolderConfigurer.

Пример для JPA:

<context:property-placeholder
    system-properties-mode="OVERRIDE" 
    location="classpath:test.properties" />

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
    destroy-method="close">
    <property name="driverClassName" value="${test.database.driver}" />
    <property name="url" value="${test.database.server}" />
    <property name="username" value="${test.database.user}" />
    <property name="password" value="${test.database.password}" />
</bean>

<bean id="entityManagerFactory"
    class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="persistenceUnitName" value="test" />
    <property name="dataSource" ref="dataSource" />
    <property name="persistenceXmlLocation"
             value="classpath:META-INF/persistence.xml" />
    <property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
    <property name="showSql" value="false" />
    <property name="generateDdl" value="${test.database.update}" />
    <property name="database" value="${test.database.databasetype}" />
</bean>
    </property>
</bean>

Теперь все, что вам нужно сделать, - это иметь разные версии test.properties в пути к классам для оперативной памяти и реальных интеграционных тестов (и, конечно, должны присутствовать соответствующие классы драйверов). Вы даже можете установить системные свойства для перезаписи значений свойств.


Если вы хотите подготовить это с помощью Maven, вы обнаружите, что копирование файлов с помощью Maven не является тривиальным. Вам потребуется способ выполнения кода, стандартными вариантами которого являются maven-antrun-plugin и gmaven-maven-plugin .

В любом случае: иметь два файла конфигурации, например, в src / main / config и добавьте два исполнения плагина, одно в фазе generate-test-resources и одно в фазе pre-integration-test. Вот версия GMaven:

<plugin>
    <groupId>org.codehaus.gmaven</groupId>
    <artifactId>gmaven-plugin</artifactId>
    <version>1.3</version>
    <executions>
        <execution>
            <phase>pre-integration-test</phase>
            <goals>
                <goal>execute</goal>
            </goals>
            <configuration>
            <source>
            new File(
               pom.build.testOutputDirectory,
               "test.properties"
            ).text = new File(
                       pom.basedir,
                       "src/main/config/int-test.properties"
            ).text;
            </source>
            </configuration>
        </execution>
        <execution>
            <phase>generate-test-resources</phase>
            <goals>
                <goal>execute</goal>
            </goals>
            <configuration>
            <source>
            new File(
               pom.build.testOutputDirectory,
               "test.properties"
            ).text = new File(
                       pom.basedir,
                       "src/main/config/memory-test.properties"
            ).text;
            </source>
            </configuration>
        </execution>
    </executions>
</plugin>
0 голосов
/ 08 октября 2013

Недавно я столкнулся с той же проблемой и, просматривая документацию для аннотации @ContextConfiguration , я заметил параметр inheritLocations .

Добавив его вмой класс, например

@ContextConfiguration(locations = { "/my_spring_integration_test.xml" }, inheritLocations=false)
public class FooIntegrationTest extends FooTest

Я обнаружил, что смог переопределить ContextConfiguration по желанию.

0 голосов
/ 28 февраля 2012

Мне не удалось использовать контекст Spring 3.x: тег property-placeholder. Я использовал старый тег bean fashion вместе с файлом свойств и смог установить соединение между моим кодом и базой данных следующим образом:

<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="location" value="/com/my/package/database.properties"/>
</bean>


<bean id="myDatasource" class="oracle.ucp.jdbc.PoolDataSourceFactory" 
    factory-method="getPoolDataSource">
    <property name="URL" value="${JDBC_URL}"/>
    <property name="user" value="${JDBC_USERNAME}"/>
    <property name="password" value="${JDBC_PASSWORD}"/>
    <property name="connectionFactoryClassName"   
      value="oracle.jdbc.pool.OracleConnectionPoolDataSource"/>
    <property name="ConnectionPoolName" value="SCDB_POOL"/>
    <property name="MinPoolSize" value="5"/>
    <property name="MaxPoolSize" value="50"/>
    <property name="connectionWaitTimeout" value="30"/>
    <property name="maxStatements" value="100"/>
</bean>

Вот пример файла свойств:

JDBC_URL=jdbc:oracle:thin:@myDB:1521:mySchema
JDBC_USERNAME=username
JDBC_PASSWORD=password

Затем я настроил свой тест JUnit так:

@ContextConfiguration(locations = {"/com/my/pkg/test-system-context.xml"})
@RunWith(SpringJUnit4ClassRunner.class)
public class HeaderDaoTest {

    @Autowired
    HeaderDao headerDao;

    @Test
    public void validateHeaderId() {
        int headerId = 0;

        headerId = headerDao.getHeaderId();

        assertNotSame(0,headerId);
    }

}

Это сработало для меня, но все делают вещи немного по-своему. Надеюсь, это поможет.

...