Mavenプロジェクトに2つ目のpersistence.xmlファイルを設定して、デプロイメントに使用される通常のファイルの代わりにテストに使用する方法はありますか?
Persistence.xmlをsrc/test/resources/META-INFに入れてみました。これはtarget/test-classes/META-INFにコピーされますが、target/classes/META-INF(src/mainからのコピー/ resources)が優先されますが、mvn -X test
クラスパスエントリを正しい順序でリストします。
[DEBUG] Test Classpath :
[DEBUG] /home/uqpbecke/dev/NetBeansProjects/UserManager/target/test-classes
[DEBUG] /home/uqpbecke/dev/NetBeansProjects/UserManager/target/classes
[DEBUG] /home/uqpbecke/.m2/repository/junit/junit/4.5/junit-4.5.jar
...
JPA構成の展開バージョンを変更せずに、理想的にはプロジェクトチェックアウトの直後にローカル調整を必要とせずに、単純なhsqldb構成に対してテストを実行できるようにしたいと思います。
以下はMaven 2.1+で動作します(それ以前は、テストとパッケージの間に実行をバインドできるフェーズがありませんでした)。
テスト中にmaven-antrun-pluginを使用してpersistence.xmlをテストバージョンに置き換え、プロジェクトがパッケージ化される前に適切なバージョンを復元できます。
この例では、実稼働バージョンがsrc/main/resources/META-INF/persistence.xmlであり、テストバージョンがsrc/test/resources/META-INF/persistence.xmlであるため、これらはtarget/classes/METAにコピーされます。 -INFおよびtarget/test-classes/META-INFそれぞれ。
これをモジョにカプセル化する方がよりエレガントですが、ファイルをコピーするだけなので、やり過ぎのように見えます。
<plugin>
<artifactId>maven-antrun-plugin</artifactId>
<version>1.3</version>
<executions>
<execution>
<id>copy-test-persistence</id>
<phase>process-test-resources</phase>
<configuration>
<tasks>
<!--backup the "proper" persistence.xml-->
<copy file="${project.build.outputDirectory}/META-INF/persistence.xml" tofile="${project.build.outputDirectory}/META-INF/persistence.xml.proper"/>
<!--replace the "proper" persistence.xml with the "test" version-->
<copy file="${project.build.testOutputDirectory}/META-INF/persistence.xml" tofile="${project.build.outputDirectory}/META-INF/persistence.xml"/>
</tasks>
</configuration>
<goals>
<goal>run</goal>
</goals>
</execution>
<execution>
<id>restore-persistence</id>
<phase>prepare-package</phase>
<configuration>
<tasks>
<!--restore the "proper" persistence.xml-->
<copy file="${project.build.outputDirectory}/META-INF/persistence.xml.proper" tofile="${project.build.outputDirectory}/META-INF/persistence.xml"/>
</tasks>
</configuration>
<goals>
<goal>run</goal>
</goals>
</execution>
</executions>
</plugin>
EE6/CDI/JPAプロジェクトでは、テストsrc/test/resources/META-INF/persistence.xml
が追加設定なしで問題なくピックアップされます。
SpringでJPAを使用する場合、テストに使用されるアプリケーションコンテキストでは以下が機能します。
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<!--
JPA requires META-INF/persistence.xml, but somehow prefers the one
in classes/META-INF over the one in test-classes/META-INF. Spring
to the rescue, as it allows for setting things differently, like by
referring to "classpath:persistence-TEST.xml". Or, simply referring
to "META-INF/persistence.xml" makes JPA use the test version too:
-->
<property name="persistenceXmlLocation" value="META-INF/persistence.xml" />
<!-- As defined in /src/test/resources/META-INF/persistence.xml -->
<property name="persistenceUnitName" value="myTestPersistenceUnit" />
<property name="jpaVendorAdapter">
<bean
class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
</bean>
</property>
</bean>
ここでは、/src/test/resources/META-INF/persistence.xml
(target/test-classes
にコピー)は/src/main/resources/META-INF/persistence.xml
(target/classes
にコピー)よりも優先されます。
残念ながら、persistence.xml
ファイルの場所によって、いわゆる「 永続性ユニットのルート 」も決まります。これにより、@Entity
アノテーションをスキャンするクラスが決まります。したがって、/src/test/resources/META-INF/persistence.xml
を使用すると、target/test-classes
のクラスではなくtarget/classes
のクラスがスキャンされます(テストが必要なクラスが存在する場所)。
したがって、テストのために、<class>
エントリをpersistence.xml
に明示的に追加して、Java.lang.IllegalArgumentException: Not an entity: class ...
を回避する必要があります。 <class>
エントリの必要性は、persistence-TEST.xml
などの別のファイル名を使用することで回避でき、そのファイルを通常のpersistence.xml
ファイルとまったく同じフォルダーに配置します。テストフォルダーのSpringコンテキストは<property name="persistenceXmlLocation" value="META-INF/persistence-TEST.xml" />
を参照するだけで、Springはsrc/main
で検索します。
別の方法として、実際のアプリケーションとテストでpersistence.xml
を同じに保ち、src/main
で1つだけを定義できる場合があります。ドライバー、方言、オプションの資格情報などのほとんどの構成は、代わりにSpringコンテキストで実行できます。また、hibernate.hbm2ddl.auto
などの設定は コンテキストで渡す にできます。
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<!-- For example: com.mysql.jdbc.Driver or org.h2.Driver -->
<property name="driverClassName" value="#{myConfig['db.driver']}" />
<!-- For example: jdbc:mysql://localhost:3306/myDbName or
jdbc:h2:mem:test;DB_CLOSE_DELAY=-1 -->
<property name="url" value="#{myConfig['db.url']}" />
<!-- Ignored for H2 -->
<property name="username" value="#{myConfig['db.username']}" />
<property name="password" value="#{myConfig['db.password']}" />
</bean>
<bean id="jpaAdaptor"
class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<!-- For example: org.hibernate.dialect.MySQL5Dialect or
org.hibernate.dialect.H2Dialect -->
<property name="databasePlatform" value="#{myConfig['db.dialect']}" />
</bean>
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="jpaVendorAdapter" ref="jpaAdapter" />
<property name="jpaProperties">
<props>
<!-- For example: validate, update, create or create-drop -->
<prop key="hibernate.hbm2ddl.auto">#{myConfig['db.ddl']}</prop>
<prop key="hibernate.show_sql">#{myConfig['db.showSql']}</prop>
<prop key="hibernate.format_sql">true</prop>
</props>
</property>
</bean>
複数のpersistence.xmlファイルはJPAの一般的な問題であり、クラスローディングのトリックによってのみ解決されるようです。
私のために働く回避策は、1つのpersistence.xmlファイルで複数の永続性ユニットを定義してから、デプロイメントとテストコードが異なるバインディングを使用することを確認することです(Springでは、エンティティマネージャファクトリで「persistenceUnitName」プロパティを設定できます)。テスト構成で展開ファイルを汚染しますが、気にしない場合は正常に動作します。
テスト用のpersistance.xmlを追加します:/src/test/resources/META-INF/persistence.xml
@Arjanが言ったように、それは 永続化ユニットのルート を変更し、エンティティクラスはターゲット/テストクラスでスキャンされます。それを処理するには、このpersistance.xmlにjar-file要素を追加します。
/src/test/resources/META-INF/persistence.xml
<persistence 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_2_0.xsd"
version="2.0">
<persistence-unit name="com.some.project">
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<jar-file>${project.basedir}/target/classes</jar-file>
<properties>
<property name="javax.persistence.jdbc.url" value="jdbc:postgresql://localhost:5432/test_database" />
<property name="javax.persistence.jdbc.driver" value="org.postgresql.Driver" />
<property name="javax.persistence.jdbc.user" value="user" />
<property name="javax.persistence.jdbc.password" value="..." />
</properties>
</persistence-unit>
</persistence>
次に、テストリソースのフィルタリングをpom.xmlに追加します。
<project>
...
<build>
...
<testResources>
<testResource>
<directory>src/test/resources</directory>
<filtering>true</filtering>
</testResource>
</testResources>
...
</build>
...
</project>
jar-fileは、jarファイルだけでなくディレクトリを対象とすることができるため、これは機能します。
ClassLoaderProxyアプローチを試しましたが、JPA注釈付きクラスがhibernateによって永続クラスとして処理されないという問題がありました。
したがって、persistence.xmlを使用せずに試してみることにしました。利点は、MavenビルドとEclipse JUnitテストが修正なしで機能することです。
JUnitテスト用の永続的なサポートクラスがあります。
public class PersistenceTestSupport {
protected EntityManager em;
protected EntityTransaction et;
/**
* Setup the the {@code EntityManager} and {@code EntityTransaction} for
* local junit testing.
*/
public void setup() {
Properties props = new Properties();
props.put("hibernate.hbm2ddl.auto", "create-drop");
props.put("hibernate.dialect", "org.hibernate.dialect.MySQLDialect");
props.put("hibernate.connection.url", "jdbc:mysql://localhost/db_name");
props.put("hibernate.connection.driver_class", "com.mysql.jdbc.Driver");
props.put("hibernate.connection.username", "user");
props.put("hibernate.connection.password", "****");
Ejb3Configuration cfg = new Ejb3Configuration();
em = cfg.addProperties(props)
.addAnnotatedClass(Class1.class)
.addAnnotatedClass(Class2.class)
...
.addAnnotatedClass(Classn.class)
.buildEntityManagerFactory()
.createEntityManager();
et = em.getTransaction();
}
}
私のテストクラスは、PersistenceTestSupportを拡張し、TestCase.setup()のsetup()を呼び出すだけです。
唯一の欠点は、永続クラスを最新の状態に保つことですが、JUnitテストではこれは許容できます。
リッチセラーとしてテストとプロダクションに異なるpersistence.xmlを使用するソリューションが好きです post (ありがとう!!)。
しかし、変更する必要があります:
<copy file="${project.build.outputDirectory}/META-INF/persistence.xml.proper" tofile="${project.build.outputDirectory}/META-INF/persistence.xml"/>
ために:
<move file="${project.build.outputDirectory}/META-INF/persistence.xml.proper" tofile="${project.build.outputDirectory}/META-INF/persistence.xml" overwrite="true"/>
Persistence.xml.properが.jarファイルに埋め込まれないため
Persistence.xmlファイルのコピーを2つ保持します。 1つはテスト用、もう1つは通常のビルド用です。
デフォルトのライフサイクルでは、build persistence.xmlをsrc/test/resources/META-INFにコピーします
実行時にテストのpersistence.xmlをsrc/test/resources/META-INFにコピーする別のプロファイルを作成します
この答えは愚かに聞こえるかもしれませんが、Run As
-> JUnit Test
。これは私がそれを作った方法です:
@BeforeClass
public static void setUp() throws IOException {
Files.copy(new File("target/test-classes/META-INF/persistence.xml"), new File("target/classes/META-INF/persistence.xml"));
// ...
}
Test/persistence.xmlをclasses/persistence.xmlにコピーしています。これは動作します。
Persistence.xmlは、すべてのクラスを明示的にリストして追加する場合を除き、エンティティクラスを検索する開始点として使用されます。したがって、このファイルを別のファイル(src/test/resourcesなど)でオーバーライドする場合は、この2番目のpersistence.xmlですべてのエンティティクラスを指定する必要があります。指定しないと、エンティティクラスが見つかりません。
別の解決策は、maven-resources-pluginを使用してファイルを上書きすることです( 'copy-resources'ゴール)。しかし、その後、テスト用に1回(たとえば、フェーズプロセステストクラス)と実際のパッケージングに1回(フェーズ 'prepare-package')2回上書きする必要があります。
このユースケースの別のオプションは、複数の永続性ユニットを追加することです。1つは本番用、もう1つはEntityManagerFactoryをテストして注入するためのものです。
両方のpersistence-unitsを実際のプロジェクトのpersistence.xmlに配置し、テストケースに正しいEntityManagerを注入させます。以下の例は、guiceでそれを行う方法を示しています。完全を期すためにいくつかのモキトのモックを残しました。モキト固有のコードはそれに応じてマークされており、インジェクションには必要ありません。
public class HibernateTestDatabaseProvider extends AbstractModule {
private static final ThreadLocal<EntityManager> ENTITYMANAGER_CACHE = new ThreadLocal<>();
@Override
public void configure() {
}
@Provides
@Singleton
public EntityManagerFactory provideEntityManagerFactory() {
return Persistence.createEntityManagerFactory("my.test.persistence.unit");
}
@Provides
public CriteriaBuilder provideCriteriaBuilder(EntityManagerFactory entityManagerFactory) {
return entityManagerFactory.getCriteriaBuilder();
}
@Provides
public EntityManager provideEntityManager(EntityManagerFactory entityManagerFactory) {
EntityManager entityManager = ENTITYMANAGER_CACHE.get();
if (entityManager == null) {
// prevent commits on the database, requires mockito. Not relevant for this answer
entityManager = spy(entityManagerFactory.createEntityManager());
EntityTransaction et = spy(entityManager.getTransaction());
when(entityManager.getTransaction()).thenReturn(et);
doNothing().when(et).commit();
ENTITYMANAGER_CACHE.set(entityManager);
}
return entityManager;
}
}
OpenEJBを使用する場合、persistence.xmlはalternate descriptorsでオーバーライドできます: http://tomee.Apache.org/alternate-descriptors。 html
別のアプローチは、テスト用に別個のpersistence.xmlを使用することです(test /../ META-INF/persistence.xmlが、スキャナーを次のようにオーバーライドします。-
persistence.xmlのテストには
<property name="hibernate.ejb.resource_scanner" value = "...TestScanner" />
新しいクラスのTestScannerのコードは次のとおりです。
import Java.lang.annotation.Annotation;
import Java.net.MalformedURLException;
import Java.net.URL;
import Java.util.Set;
import org.hibernate.ejb.packaging.NamedInputStream;
import org.hibernate.ejb.packaging.NativeScanner;
public class TestScanner extends NativeScanner
{
@Override
public Set <Class <?> >
getClassesInJar (URL jar, Set <Class <? extends Annotation> > annotations)
{ return super.getClassesInJar (getUpdatedURL (jar), annotations); }
@Override
public Set <NamedInputStream>
getFilesInJar (URL jar, Set <String> patterns)
{ return super.getFilesInJar (getUpdatedURL (jar), patterns); }
@Override
public Set <Package>
getPackagesInJar (URL jar, Set <Class <? extends Annotation> > annotations)
{ return super.getPackagesInJar (getUpdatedURL (jar), annotations); }
private URL getUpdatedURL (URL url)
{
String oldURL = url.toExternalForm ();
String newURL = oldURL.replaceAll ("test-classes", "classes");
URL result;
try {
result = newURL.equals (oldURL) ? url : new URL (newURL);
} catch (MalformedURLException e)
{ // Whatever }
return result;
}
}
私は同じことをしようとしています。私に合ったソリューションがあります-あなたのソリューションは異なる場合があります(そして、あなたはそのソリューションを好きではないかもしれません...それは少し低レベルです)。
ネット上の記事に出会ったが、そこで彼らはカスタムクラスローダーを使用して、インスピレーションの役目を果たす同様のことをしていました。誰も改善方法を見ることができれば、提案は大歓迎です。展開にはEntityManagerのコンテナインジェクションに依存しますが、テストにはこのコードを使用して自分で作成します。
final Thread currentThread = Thread.currentThread();
final ClassLoader saveClassLoader = currentThread.getContextClassLoader();
currentThread.setContextClassLoader(new ClassLoaderProxy(saveClassLoader));
EntityManagerFactory emFactory = Persistence.createEntityManagerFactory("test");
em = emFactory.createEntityManager();
次に、ClassLoaderProxyは取得できる限り最小限であり、META-INF/persistence.xmlのリクエストをMETA-INF/test-persist.xmlにリダイレクトするだけです。
public class ClassLoaderProxy extends ClassLoader {
public ClassLoaderProxy(final ClassLoader parent) {
super();
}
@Override
public Enumeration<URL> getResources(final String name) throws IOException {
if (!"META-INF/persistence.xml".equals(name)) {
return super.getResources(name);
} else {
System.out.println("Redirecting persistence.xml to test-persist.xml");
return super.getResources("META-INF/test-persist.xml");
}
}
}
これをもう少し説明するために:
HibernateはHibernateをロードするために使用されたクラスローダーに(何らかの形で)戻るので、少なくともいくつかの問題が発生していました(少なくともそれが起こっていたと思います)。 ClassLoader切り替えコード(最初のブロック)をテストケースに静的ブロックとして配置すると、Hibernateの前にロードされますが、ユニットテスト構造によっては、他の場所にも同じコードを配置する必要がある場合があります。 (うん)。
これは、クラスパス上の複数のpersistence.xmlファイルを検出し、状態の復元を事前にテストするHibernateの適切な処理に関するRich Sellerの回答の拡張です。
展開/パッケージング用に1つの永続性ファイルを作成し、テスト用に1つを作成します。
src/main/resources/persistence.xml
src/テスト/resources/persistence -テスト。xml
pom.xmlで、これをプラグインセクションに追加します。
<plugin>
<artifactId>maven-antrun-plugin</artifactId>
<version>1.3</version>
<executions>
<execution>
<id>copy-test-persistence</id>
<phase>process-test-resources</phase>
<configuration>
<tasks>
<echo>renaming deployment persistence.xml</echo>
<move file="${project.build.outputDirectory}/META-INF/persistence.xml" tofile="${project.build.outputDirectory}/META-INF/persistence.xml.proper"/>
<echo>replacing deployment persistence.xml with test version</echo>
<copy file="${project.build.testOutputDirectory}/META-INF/persistence-testing.xml" tofile="${project.build.outputDirectory}/META-INF/persistence.xml" overwrite="true"/>
</tasks>
</configuration>
<goals>
<goal>run</goal>
</goals>
</execution>
<execution>
<id>restore-persistence</id>
<phase>prepare-package</phase>
<configuration>
<tasks>
<echo>restoring the deployment persistence.xml</echo>
<move file="${project.build.outputDirectory}/META-INF/persistence.xml.proper" tofile="${project.build.outputDirectory}/META-INF/persistence.xml" overwrite="true"/>
</tasks>
</configuration>
<goals>
<goal>run</goal>
</goals>
</execution>
</executions>
</plugin>
persistence.xmlを使用して独自のMavenプロジェクトにテストを配置する
Database.proprertiesファイルをフィルター処理し、プロファイルごとに1つのdatabase.propertiesを作成できる異なるMavenプロファイルを使用することをお勧めします。
これにより、.propertiesを除く他の構成ファイルの複製を保持する必要がなくなります。
<properties>
<!-- Used to locate the profile specific configuration file. -->
<build.profile.id>default</build.profile.id>
<!-- Only unit tests are run by default. -->
<skip.integration.tests>true</skip.integration.tests>
<skip.unit.tests>false</skip.unit.tests>
<integration.test.files>**/*IT.Java</integration.test.files>
</properties>
<profiles>
<profile>
<id>default</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<properties>
<!--
Specifies the build profile id, which is used to find out the correct properties file.
This is not actually necessary for this example, but it can be used for other purposes.
-->
<build.profile.id>default</build.profile.id>
<skip.integration.tests>true</skip.integration.tests>
<skip.unit.tests>false</skip.unit.tests>
</properties>
<build>
<filters>
<!--
Specifies path to the properties file, which contains profile specific
configuration. In this case, the configuration file should be the default spring/database.properties file
-->
<filter>src/main/resources/META-INF/spring/database.properties</filter>
</filters>
<resources>
<!--
Placeholders found from files located in the configured resource directories are replaced
with values found from the profile specific configuration files.
-->
<resource>
<filtering>true</filtering>
<directory>src/main/resources</directory>
<!--
You can also include only specific files found from the configured directory or
exclude files. This can be done by uncommenting following sections and adding
the configuration under includes and excludes tags.
-->
<!--
<includes>
<include></include>
</includes>
<excludes>
<exclude></exclude>
</excludes>
-->
</resource>
</resources>
</build>
</profile>
<profile>
<id>integration</id>
<properties>
<!--
Specifies the build profile id, which is used to find out the correct properties file.
This is not actually necessary for this example, but it can be used for other purposes.
-->
<build.profile.id>integration</build.profile.id>
<skip.integration.tests>false</skip.integration.tests>
<skip.unit.tests>true</skip.unit.tests>
</properties>
<build>
<filters>
<!--
Specifies path to the properties file, which contains profile specific
configuration. In this case, the configuration file is searched
from spring/profiles/it/ directory.
-->
<filter>src/main/resources/META-INF/spring/profiles/${build.profile.id}/database.properties</filter>
</filters>
<resources>
<!--
Placeholders found from files located in the configured resource directories are replaced
with values found from the profile specific configuration files.
-->
<resource>
<filtering>true</filtering>
<directory>src/main/resources</directory>
<!--
You can also include only specific files found from the configured directory or
exclude files. This can be done by uncommenting following sections and adding
the configuration under includes and excludes tags.
-->
<!--
<includes>
<include></include>
</includes>
<excludes>
<exclude></exclude>
</excludes>
-->
</resource>
</resources>
</build>
</profile>
</profiles>
単体テストではsurefire、統合テストではfailsfeの助けを借りて完了です。
<plugin>
<groupId>org.Apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.12</version>
<configuration>
<junitArtifactName>org.junit:com.springsource.org.junit</junitArtifactName>
<!--see: https://issuetracker.springsource.com/browse/EBR-220-->
<printSummary>false</printSummary>
<redirectTestOutputToFile>true</redirectTestOutputToFile>
<!-- Skips unit tests if the value of skip.unit.tests property is true -->
<skipTests>${skip.unit.tests}</skipTests>
<!-- Excludes integration tests when unit tests are run. -->
<excludes>
<exclude>${integration.test.files}</exclude>
</excludes>
</configuration>
</plugin>
<plugin>
<groupId>org.Apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<version>2.12</version>
<configuration>
<!-- Skips integration tests if the value of skip.integration.tests property is true -->
<skipTests>${skip.integration.tests}</skipTests>
<includes>
<include>${integration.test.files}</include>
</includes>
<forkMode>once</forkMode>
<!--
<reuseForks>false</reuseForks>
<forkCount>1</forkCount>
-->
</configuration>
<executions>
<execution>
<id>integration-test</id>
<goals>
<goal>integration-test</goal>
</goals>
</execution>
<execution>
<id>verify</id>
<goals>
<goal>verify</goal>
</goals>
</execution>
</executions>
</plugin>
必要なのはmvn test
単体テスト用およびmvn verify -Pintegration
統合テスト用。明らかに、指定された(プロファイル上の)パス(または他の場所でパスを変更する)にdatabase.propertiesファイルを作成する必要があります。