Формула для тестирования DAO с Hibernate / JPA Mappings и JUnit - PullRequest
3 голосов
/ 17 декабря 2011

Я нашел много вопросов, связанных с этим, но многие обсуждают использование баз данных HSQL вместо настройки Eclipse и командной строки для запуска реальных тестов.

Скажем, у меня есть следующий составсценарий:

@Entity
@Table("Team")
public class Team {
    private Integer id;
    private String name;
    private String city;
    private String owner;
    private List<Player> players;

    @Column(name="TEAM_ID")
    public Integer getTeamId() {
       return this.id;
    }

    public void setTeamId(Integer id) {
       this.id = id;
    }

    @Column(name="TEAM_ID")
    public Integer getName() {
       return this.name;
    }

    public void setName(String name) {
       this.name = name;
    }

    // ... etc.
}

Допустим, у нас есть следующий уровень доступа к данным:

@Repository
public class TeamDao {

    @PersistenceContext(unitName="examplePU")
    EntityManger em;

    public Team saveOrUpdate(Team team) {
        this.em.merge(team);
    }

    // etc...
}

Я бы хотел написать интеграционный тест, подобный следующему:

public class TeamIntegrationTest {

    @Resource
    TeamDao teamDao;

    @Test
    public void resourceLoaded() {
        assertNotNull(teamDao);
    }

    @Test
    public void testInsertTeam() {
        Team team = new Team();
        team.setName("Packers");
        team.setCity("Green Bay");
        teamDao.saveOrUpdate(team);
    }
}

Что требуется для запуска этого в моей собственной среде разработки, прежде чем принимать меры для обеспечения его правильной работы?Пожалуйста, включите ссылки на соответствующие библиотеки, которые могут помочь.

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


Предлагаемая формула

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

Прежде всего, конфигурация (при условии src/{main,test,inttest}/java, src/{main,test,inttest}/resources setup:

src/resources/java
|
| - META-INF
|    |
|    | - orm.xml
|    | - persistence.xml
| - applicationContext.xml
| - applicationContext-Integration.xml

orm.xml не содержит много контента, но у меня возникли проблемы, когда его там не было:

<?xml version="1.0" encoding="UTF-8"?>
<entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm
          http://java.sun.com/xml/ns/persistence/orm_1_0.xsd" version="1.0">
</entity-mappings>

Persistence.xml содержит объекты, о которых JPA должен знать:

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="1.0" xmlns="http://java.sun.com/xml/ns/persistence"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence 
        http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">
    <persistence-unit name="persistenceUnitName">
        <class>com.acme.project.className</class>
        <!-- Etc... -->
    </persistence-unit>
</persistence>

applicationContext.xml содержит основную часть конфигурации:

<?xml version="1.0" encoding="UTF-8"?>
<!-- Spring configuration for JPA/database/Transactions -->
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:p="http://www.springframework.org/schema/p"
    xsi:schemaLocation="
            http://www.springframework.org/schema/beans
                http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
            http://www.springframework.org/schema/context
                http://www.springframework.org/schema/context/spring-context-3.0.xsd
            http://www.springframework.org/schema/aop
                http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
            http://www.springframework.org/schema/tx
                http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">

    <context:annotation-config/>

    <context:component-scan base-package="com.acme.project.full.package.name" />

    <tx:annotation-driven transaction-manager="transactionManager"/>

    <bean id="transactionManager"
        class="org.springframework.orm.jpa.JpaTransactionManager"
        p:entityManagerFactory-ref="entityManagerFactory" p:dataSource-ref="jndiDataSource"
        p:jpaDialect-ref="jpaDialect" />

    <bean id="jndiDataSource"
        class="org.springframework.jdbc.datasource.DriverManagerDataSource">
            <property name="driverClassName" value="com.databaseType.jdbc.Driver" />
            <property name="url" value="jdbc:url://location:port/databaseName" />
            <property name="username" value="" />
            <property name="password" value="" />
    </bean>

    <bean id="jpaDialect" class="org.springframework.orm.jpa.vendor.HibernateJpaDialect" />

    <bean id="entityManagerFactory"
        class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
        lazy-init="true" p:dataSource-ref="jndiDataSource"
        p:jpaVendorAdapter-ref="hibernateJpaVendorAdapter"
        p:persistenceUnitName="persistenceUnitName" p:jpaDialect-ref="jpaDialect">
    </bean>

    <bean id="hibernateJpaVendorAdapter"
        class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"
        p:databasePlatform="org.hibernate.dialect.MySQLDialect" p:generateDdl="false"
        p:showSql="true" />

Мы используем EntityManager, поэтому рекомендованное решение AbstractTransactionalDataSourceSpringContextTests не будет работать длянас. Вместо этого мы создали следующее, которое извлекает SpringJUnit4ClassRunner.class и правильно устанавливает applicationContext.

@ContextConfiguration(locations = { "classpath:applicationContext-JPA-Integration.xml" })
@RunWith(SpringJUnit4ClassRunner.class)
public abstract class SpringBasedIntegration { }

Мы создали этот универсальный класс, чтобы его можно было использовать в других классах интеграционных тестов,

public class TeamIntegrationTest extends SpringBasedIntegration {

    @Resource
    TeamDao vitalDao;

    @Before() 
    public void before() {
        List<Team> teams = teamDao.getKnownValues("...");
        for(Team entry : teams) {
            vitalDao.deleteTeam(team);
        }
    }

    @Test
    public void testSomething() { 
        // vitalDao should be initialized with an entityManger!
    }

Теперь, у этого есть слабая сторона отсутствия отката данных, поэтому я ввел хак, который удалит известные значения с помощью метода @Before, чтобы у каждого метода было свежее известное состояние,Это может работать для некоторых, но, вероятно, не для всех.

Все отзывы приветствуются.Я бы хотел, чтобы сообщество имело хорошо документированное решение для интеграционного тестирования JPA / Hibernate, и мы все знаем, что StackOverflow - лучший первый результат в Google для наших запросов.

Ответы [ 3 ]

4 голосов
/ 17 декабря 2011

Некоторое дополнение к хорошему материалу, предоставленному Карлосом.Если вы хотите совместить Java EE и не хотите привязывать свое приложение к Spring, вы можете использовать только HSQLDB + Maven.

В руководствах, предоставленных Карлосом, уже говорится об этой комбинации, но вы неВесна не нужна, если ты не хочешь.Вы можете просто создать PersistentContext программно, создав базовый тестовый пример JUnit, который сделает это за вас:

@Ignore
public abstract class DaoBaseTestCase {

    private static final String TEST_UNIT_NAME = "test_unit";
    protected static EntityManagerFactory entityManagerFactory;
    protected static EntityManager entityManager;

    @BeforeClass
    public static void prepareEntityManagerFactory() {
        entityManagerFactory = Persistence.createEntityManagerFactory(TEST_UNIT_NAME);
        entityManager = entityManagerFactory.createEntityManager();
        entityManager.getTransaction().begin();
    }

    @After
    public abstract void rollbackTransactionAndReleaseEntityManager();

    @AfterClass
    public static void releaseEntityManager() {
        if (entityManager.getTransaction().isActive()) {
            entityManager.getTransaction().rollback();
        }

        if(entityManager.isOpen()) {
            entityManager.close();
        }
        entityManagerFactory.close();
    }
}

Затем вы можете создать методы получения / установки для EntityManager в ваших DAO, чтобы передать тот, который был создан в базеDAO test.

4 голосов
/ 17 декабря 2011

Попробуйте Spring Framework,

Вы можете использовать Spring Transactional Test для своих классов JUNIT;он запускает тест, но каждое изменение откатывается после завершения операции.Вы можете изменить это поведение, чтобы на самом деле фиксировать, это зависит от вас.

Волшебство создается классом AbstractTransactionalDataSourceSpringContextTests , в вашем случае вам потребуется настроить applicationcontext в локальном проекте и запустить тест с помощью eclipse junit runner.

Это хороший учебник, использующий AbstractTransactionalDataSourceSpringContextTests http://zenoconsulting.wikidot.com/blog:8

Другие ссылки http://www.infoq.com/articles/testing-in-spring http://static.springsource.org/spring/docs/2.5.x/reference/testing.html#testcontext-support-classes-junit44

2 голосов
/ 30 октября 2013

Я недавно опубликовал облегченную среду под названием TestFun-JEE для тестирования кода EJB + JPA ++ с использованием JUnit - эта среда заботится о том, чтобы вставлять EJB и Mockito mocks в ваш тестовый код без использования контейнера (возможно,Я должен сказать, что фреймворк издевается над контейнером).

@Local
public interface SomeDao {
    SomeEntity save(SomeEntity t);
    List<SomeEntity> getAll();
}

@Stateless
public class SomeDaoImpl implements SomeDao {

    @PersistenceContext(unitName = "TestFun")
    private EntityManager entityManager;

    @Override
    public SomeEntity save(SomeEntity t) {
        if (t.getId() == 0) {
            entityManager.persist(t);
        } else {
            entityManager.merge(t);
        }
        return t;
    }

    @SuppressWarnings("unchecked")
    @Override
    public List<SomeEntity> getAll() {
        Query query = entityManager.createQuery("FROM SomeEntity AS be");
        return query.getResultList();
    }

}

@RunWith(EjbWithMockitoRunner.class)
public class DaoTest {

    @EJB
    private SomeDao dao;

    @Test
    public void saveOne() {
        dao.save(new SomeEntity(0, "1. one", "s"));
        List<SomeEntity> entities = dao.getAll();
        assertEquals(1, entities.size());
    }

    @Test
    public void getMany() {
        dao.save(new SomeEntity(0, "1. one", "s"));
        dao.save(new SomeEntity(0, "2. two", "r"));
        List<SomeEntity> entities = dao.getAll();
        assertEquals(2, entities.size());
        assertThat(entities).
                onProperty("name").
                isEqualTo(Arrays.asList("1. one", "2. two"));//Note, this using org.fest.assertions.Assertions.assertThat
    }

}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...