интеграционное тестирование с JPA и Spring - PullRequest
1 голос
/ 24 марта 2011

У меня есть веб-приложение Spring / JPA, для которого я хотел бы написать несколько тестов.В идеале я хотел бы иметь возможность:

  • создать схему тестовой БД (из аннотированных классов JPA) один раз перед запуском тестов
  • запустить каждый метод теста в своей собственной транзакциикоторый откатывается после завершения теста
  • указывает набор данных (DbUnit), который будет загружен для каждого теста на уровне класса или метода.Тестовые данные должны быть загружены после начала транзакции, так что тестовые данные также будут откатываться после завершения теста
  • вставка бобов Spring в класс теста

I'mЯ знаю, что Spring предоставляет классы, которые могут обеспечить транзакционное поведение, которое я ищу.В идеале, окончательное решение должно выглядеть примерно так:

// This dataset will be used for all tests that don't override it with their own annotation
@TestData('/dbunit/dataSetDefault.xml')
public class MyTests extends ProbablySomethingFromTheSpringFramework {

  @Test
  void testWithDefaultDataSet() {
    // Transaction is implicitly started here
    // My test code goes here
    // A transaction is implicitly rolled-back here    
  }

  @TestData('/dbunit/dataSetCustom.xml')
  @Test
  void testWithCustomDataSet() {
    // Same as the other test
  }
}

Очевидно, что родительский класс и @TestData являются вымышленными, есть ли что-то доступное, обеспечивающее нужную мне функциональность?* Это оставляет вопрос о том, как создать тестовую схему БД.В идеале это должно происходить один раз перед запуском всех тестов (Maven).Может кто-нибудь подсказать, как мне этого добиться?Я предполагаю, что это включает в себя использование чего-то для преобразования аннотаций JPA в DDL, а затем что-то еще для загрузки этого в схему тестовой базы данных.

Спасибо!

Ответы [ 2 ]

4 голосов
/ 24 марта 2011

В идеале я хотел бы иметь возможность:

  • создать схему тестовой БД (из аннотированных классов JPA) один раз до запуска тестов

По крайней мере, Hibernate позволяет создавать базу данных из аннотированных классов, я думаю, что другие реализации JPA тоже будут работать.

  • запускать каждый метод тестирования по-своемутранзакция, которая откатывается после завершения теста

См. @ TransactionConfiguration и значение defaultRollback , а также AbstractTransactionalJUnit4SpringContextTests (есть также похожие абстрактные классы, по крайней мере, для JUnit 3.8 и TestNG), внимательно посмотрите на См. также -разделы в javadocs, они указывают на множество очень полезных связанных классов и аннотаций.

  • задает набор данных (DbUnit), который будет загружен для каждого теста на уровне класса или метода.Тестовые данные должны быть загружены после начала транзакции, чтобы тестовые данные также были откатаны после завершения теста

Я фактически никогда не использовал DbUnit, но по крайней мере сJUnit, вы можете использовать @ Before и @ BeforeClass для запуска методов перед каждым тестом и классом соответственно (есть также @After и @AfterClass).Если у вас есть иерархия классов, методы, аннотированные @ Before / @ BeforeClass, запускаются в порядке расширения (сначала базовый класс).Для запуска sql-сценариев см., Например, SimpleJdbcTestUtils .

  • для внедрения Spring-бинов в тестовый класс

AbstractTransactionalJUnit4SpringContextTests isApplicationContextAware, также см. @ ContextConfiguration для настройки.

Наконец, вот немного урезанный базовый класс, который я использую для расширения моих реальных интеграционных тестов (Spring 3, JUnit4, Hibernate как JPA-провайдер, если это имеет значение):

    //test-context, same as normal context, except uses H2 for in-memory database and has some stuff for faking session- and request-scope
    @ContextConfiguration(locations="classpath:applicationContext-test.xml") 
    @TransactionConfiguration(transactionManager="txManager", defaultRollback=true)
    @Transactional
    public abstract class IntegrationTestBase extends AbstractTransactionalJUnit4SpringContextTests
    {
        @PersistenceContext
        protected EntityManager em; 

        @Autowired
        protected SomeService serviceAvailableToSubclasses;

        @Before
        public void beforeEachTest()
        {
            //fill database with testdata and whatever you need to, runs before each test in extending classes
        }

        @After
        public void afterEachTest()
        {
            //Do something, if you need to, or just remove this
        }

    }

Исходя из этого, вы можете использовать @Transactional, @Autowired и т. д. в своих производных классах или получить более конкретные абстрактные тестовые базовые классы (у меня есть, например, IntegrationSessionTestBase иIntegrationSessionNewRequestPerTestBase для различных видов тестов, требующих новых сеансов и / или запросов на тест).

1 голос
/ 24 марта 2011

Я сделал это с помощью простого приложения на основе JPA (Hibernate).

<project
  xmlns="http://maven.apache.org/POM/4.0.0"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">

  <modelVersion>4.0.0</modelVersion>

  <groupId>com.soebes.casestudy</groupId>
  <artifactId>casestudy</artifactId>
  <packaging>jar</packaging>
  <version>0.0.1-SNAPSHOT</version>

  <name>Case Study Pizza Ordering</name>
  <url>Pizza Ordering</url>

  <properties>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

    <hibernate-core-version>3.4.0.GA</hibernate-core-version>
    <database.driverClassName>com.mysql.jdbc.Driver</database.driverClassName>
    <database.url>jdbc:mysql://localhost:3306/casestudy</database.url>
    <database.dialect>org.hibernate.dialect.MySQLDialect</database.dialect>
    <database.root.user>root</database.root.user>
    <database.root.password>root</database.root.password>

    <database.user>casestudy</database.user>
    <database.password>casestudy</database.password>

    <database.database>casestudy</database.database>
  </properties>

  <build>
    <resources>
      <resource>
        <directory>${basedir}/src/main/resources</directory>
        <filtering>true</filtering>
      </resource>
    </resources>
    <plugins>
      <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>hibernate3-maven-plugin</artifactId>
        <version>2.2</version>
        <executions>
          <execution>
            <id>hibernate-create-schema</id>
            <phase>generate-test-sources</phase>
            <goals>
              <goal>hbm2ddl</goal>
            </goals>
            <configuration>
              <components>
                <component>
                  <name>hbm2ddl</name>
                  <implementation>annotationconfiguration</implementation>
                </component>
              </components>
              <componentProperties>
                <configurationfile>/src/main/resources/hibernate.cfg.xml</configurationfile>
                <jdk5>true</jdk5>
                <packagename>com.soebes.casestudy.bo</packagename>
                <console>false</console>
                <outputfilename>create.sql</outputfilename>
                <drop>false</drop>
                <create>true</create>
                <update>false</update>
                <export>false</export>
                <format>true</format>
              </componentProperties>
            </configuration>
          </execution>
          <execution>
            <id>hibernate-drop-schema</id>
            <phase>generate-test-sources</phase>
            <goals>
              <goal>hbm2ddl</goal>
            </goals>
            <configuration>
              <components>
                <component>
                  <name>hbm2ddl</name>
                  <implementation>annotationconfiguration</implementation>
                </component>
              </components>
              <componentProperties>
                <configurationfile>/src/main/resources/hibernate.cfg.xml</configurationfile>
                <jdk5>true</jdk5>
                <packagename>com.soebes.casestudy.bo</packagename>
                <console>false</console>
                <outputfilename>drop.sql</outputfilename>
                <drop>true</drop>
                <create>false</create>
                <update>false</update>
                <export>false</export>
                <format>true</format>
              </componentProperties>
            </configuration>
          </execution>
        </executions>
        <dependencies>
          <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.9</version>
          </dependency>
        </dependencies>
      </plugin>

      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-surefire-plugin</artifactId>
        <version>2.8</version>
      </plugin>

     <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>sql-maven-plugin</artifactId>
        <version>1.4</version>

        <dependencies>
          <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.9</version>
          </dependency>
        </dependencies>

        <!-- common configuration shared by all executions -->
        <configuration>
          <driver>${database.driverClassName}</driver>
          <url>${database.url}</url>
          <username>${database.root.user}</username>
          <password>${database.root.password}</password>
        </configuration>
        <executions>
          <execution>
            <id>drop-database</id>
            <phase>generate-test-resources</phase>
            <goals>
              <goal>execute</goal>
            </goals>
            <configuration>
              <sqlCommand>
              DROP DATABASE IF EXISTS casestudy;
              CREATE DATABASE casestudy;
              GRANT ALL ON casestudy.* TO ${database.user} IDENTIFIED BY '${database.password}';
              </sqlCommand>
            </configuration>
          </execution>
          <execution>
            <id>create-database</id>
            <phase>generate-test-resources</phase>
            <goals>
              <goal>execute</goal>
            </goals>
            <configuration>
              <sqlCommand>
              USE casestudy;
              </sqlCommand>
              <srcFiles>
                <srcFile>${project.build.directory}/hibernate3/sql/create.sql</srcFile>
              </srcFiles>
            </configuration>
          </execution>
        </executions>
      </plugin>

    </plugins>
  </build>

  <dependencies>
    <dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-annotations</artifactId>
      <version>${hibernate-core-version}</version>
    </dependency>
    <dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-core</artifactId>
      <version>3.3.0.SP1</version>
    </dependency>
    <dependency>
        <groupId>javassist</groupId>
        <artifactId>javassist</artifactId>
        <version>3.4.GA</version>
    </dependency>

    <dependency>
      <groupId>org.testng</groupId>
      <artifactId>testng</artifactId>
      <version>5.14.6</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.9</version>
    </dependency>
    <dependency>
      <groupId>log4j</groupId>
      <artifactId>log4j</artifactId>
      <version>1.2.13</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-api</artifactId>
      <version>1.6.1</version>
    </dependency>
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-log4j12</artifactId>
      <version>1.6.1</version>
    </dependency>
  </dependencies>
</project>
...