如何使用 TomEE 测试 EJB?
How to test EJB with TomEE?
我正在使用 TomEE 和 Intellij 来测试我的 EJB/JPA bean。我在 this answer that I should use a embedded container to test. I discovered Arquillian on this other answer(来自同一个问题)上看到,但正如评论中所述,设置起来很困难,而且对用户不友好,这是像我这样的初学者搜索的东西。
不幸的是,我没有像回答的那样使用 glassfish-embedded-all
依赖项,而是 tomee-embedded
。我在这个 official tutorial 上看到它应该使用 JTA 以及上面的回答。但是为什么?
像上次那样做 link,我收到了这个错误:
No EJBContainer provider available: no provider names had been found.
然后使用@BeforeClass
方法中的一段代码来自这个answer。我的测试如下所示:
Properties properties = new Properties();
properties.setProperty(EJBContainer.PROVIDER, "tomee-embedded");
EJBContainer container = EJBContainer.createEJBContainer(properties);
AccountDao dao = (AccountDao) container.getContext().lookup("java:global/Test/AccountDao");
其中 Test
是我的应用程序名称,AccountDao
是我要测试的 Stateless Bean
。但是现在我收到了这个错误:
Caused by: org.hsqldb.HsqlException: user lacks privilege or object not found: PG_CLASS
虽然我没有使用 HSQLDB
,但我遇到了这个错误。如何正确添加一些 postgresql 属性以正确实例化我的 Hibernate entityManager
?这是我的 persistence.xml
:
<persistence-unit name="unitName">
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<class>entity.PersistentEntity.Account</class>
<properties>
<property name="tomee.jpa.factory.lazy" value="true"/>
<property name="javax.persistence.jdbc.driver" value="org.postgresql.Driver"/>
<property name="javax.persistence.jdbc.url" value="jdbc:postgresql://localhost/click10"/>
<property name="javax.persistence.jdbc.user" value="postgres"/>
<property name="javax.persistence.jdbc.password" value="postgres"/>
<property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQL82Dialect"/>
<property name="hibernate.show_sql" value="true"/>
<property name="hibernate.format_sql" value="true"/>
<property name="hibernate.hbm2ddl.auto" value="update"/>
</properties>
</persistence-unit>
我已成功使用 TomEE 嵌入式容器进行单元测试。与任何外部 JUnit 资源一样,它可以使用 @Rule
进行管理,因此我有两个 classes,规则 class 和嵌入式 TomEE 的包装器。
TomEE Container
class 的包装器 class。配置嵌入式 derby 数据源和用户,以便我们可以测试基本身份验证。
/**
* class for starting an Embedded TomEE server which will scan the classpath and start the application.
* The configuration configures an InMemory derby database, and tells JPA to create tables based on the Entity annotations
*
*/
public class EmbeddedTomEE {
public static final String USERNAME = "aUser";
public static final String PASSWORD = "aPassword";
private Container container;
public void start() {
Configuration configuration = new Configuration();
Properties properties = new Properties();
properties.setProperty("jdbc/UdDB", "new://Resource?type=DataSource");
properties.setProperty("jdbc/UdDB.jdbcDriver", "org.apache.derby.jdbc.EmbeddedDriver");
properties.setProperty("jdbc/UdDB.jdbcUrl", "jdbc:derby:memory:udb;create=true");
properties.setProperty("jdbc/UdDB.username", "SA");
properties.setProperty("jdbc/UdDB.password", "");
properties.setProperty("jdbc/UdDB.jtaManaged", "true");
properties.setProperty("persistence_unit.javax.persistence.schema-generation.database.action", "create");
properties.setProperty("persistence_unit.javax.persistence.sql-load-script-source", "META-INF/testdata.sql");
properties.setProperty("rest-persistence_unit.eclipselink.logging.level", "FINE"); //use 'FINE' for JPA logging
configuration.setProperties(properties);
// use a random port so we can have TomEE running parallel with tests
configuration.randomHttpPort();
configuration.setWebXml("src/main/webapp/WEB-INF/web.xml");
HashMap<String, String> users = new HashMap<>();
users.put(USERNAME, PASSWORD);
configuration.setUsers(users);
HashMap<String, String> roles = new HashMap<>();
roles.put("aUser", "user");
configuration.setRoles(roles);
container = new Container(configuration).deployClasspathAsWebApp();
}
public int getPort() {
return container.getConfiguration().getHttpPort();
}
public void stop() {
container.close();
}
}
负责在执行每个测试之前启动嵌入式 TomEE 的 JUnit 规则。我们还有一些逻辑可以避免在每次测试时启动和停止容器的成本。 class 还创建了一个 JAX-RS webClient,可用于调用应用程序 REST 服务。
/**
* JUnit rule for running an EmbeddedTomEE in memory. The rule has static state, this is to avoid starting and stopping the embedded container
* with every test. Every time no test are running we start a timer, which is canceled if another test is started. This way the rule works well for a
* single test run inside an IDE, and running multiple tests from Maven.
*
*/
public class EmbeddedTomEERule extends ExternalResource {
private static EmbeddedTomEE tomEE;
private static final AtomicInteger count = new AtomicInteger();
private static Timer timer;
@Override
protected void before() throws Throwable {
startIfNeeded();
if (timer != null) {
timer.cancel();
}
count.incrementAndGet();
}
@Synchronized
private void startIfNeeded() {
if (tomEE == null) {
tomEE = new EmbeddedTomEE();
tomEE.start();
Runtime.getRuntime().removeShutdownHook(new Thread(() -> tomEE.stop()));
}
}
@Override
protected void after() {
int runningTests = count.decrementAndGet();
if (runningTests == 0) {
// stop after some time if no new test are started
timer = new Timer();
timer.schedule(new StopEmbeddedContainer(), 10000);
}
}
public int getPort() {
return tomEE.getPort();
}
/**
* creates a new WebClient that can request data from the specified path
*/
public WebClient getWebClient(String path, MediaType mediatype) {
WebClient client = WebClient.create("http://localhost:" + tomEE.getPort() + "/", Collections.singletonList(new JohnzonProvider()),
EmbeddedTomEE.USERNAME, EmbeddedTomEE.PASSWORD, null)
.path(path).accept(mediatype);
return client;
}
private static class StopEmbeddedContainer extends TimerTask {
@Override
public void run() {
tomEE.stop();
}
}
}
这是一个测试的示例
public class ExampleTest {
@Rule
public EmbeddedTomEERule rule = new EmbeddedTomEERule();
@Test
public void doTest() {
WebClient client = rule.getWebClient("some-endpoint", MediaType.APPLICATION_JSON_TYPE);
Output dto = client.get(Input.class);
}
}
这种类型的测试允许您在 HTTP 层测试您的应用程序,并且允许您在测试和服务器代码中放置断点。从技术上讲,将其称为单元测试可能有点牵强,但在测试多个组件时,我更喜欢这种类型的测试。由于您需要一个功能齐全的 TomEE,因此您需要提供一些外部依赖项,在我的例子中它看起来像这样:
<dependency>
<groupId>org.apache.derby</groupId>
<artifactId>derby</artifactId>
<version>${derby.db.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.tomee</groupId>
<artifactId>openejb-core</artifactId>
<version>${openejb-core.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.tomee</groupId>
<artifactId>openejb-cxf-rs</artifactId>
<version>${openejb-core.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.tomee</groupId>
<artifactId>openejb-server</artifactId>
<version>${openejb-core.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.tomee</groupId>
<artifactId>openejb-rest</artifactId>
<version>${openejb-core.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.tomee</groupId>
<artifactId>tomee-embedded</artifactId>
<version>${openejb-core.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.glassfish.web</groupId>
<artifactId>el-impl</artifactId>
<version>2.2</version>
<scope>test</scope>
</dependency>
我正在使用 TomEE 和 Intellij 来测试我的 EJB/JPA bean。我在 this answer that I should use a embedded container to test. I discovered Arquillian on this other answer(来自同一个问题)上看到,但正如评论中所述,设置起来很困难,而且对用户不友好,这是像我这样的初学者搜索的东西。
不幸的是,我没有像回答的那样使用 glassfish-embedded-all
依赖项,而是 tomee-embedded
。我在这个 official tutorial 上看到它应该使用 JTA 以及上面的回答。但是为什么?
像上次那样做 link,我收到了这个错误:
No EJBContainer provider available: no provider names had been found.
然后使用@BeforeClass
方法中的一段代码来自这个answer。我的测试如下所示:
Properties properties = new Properties();
properties.setProperty(EJBContainer.PROVIDER, "tomee-embedded");
EJBContainer container = EJBContainer.createEJBContainer(properties);
AccountDao dao = (AccountDao) container.getContext().lookup("java:global/Test/AccountDao");
其中 Test
是我的应用程序名称,AccountDao
是我要测试的 Stateless Bean
。但是现在我收到了这个错误:
Caused by: org.hsqldb.HsqlException: user lacks privilege or object not found: PG_CLASS
虽然我没有使用 HSQLDB
,但我遇到了这个错误。如何正确添加一些 postgresql 属性以正确实例化我的 Hibernate entityManager
?这是我的 persistence.xml
:
<persistence-unit name="unitName">
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<class>entity.PersistentEntity.Account</class>
<properties>
<property name="tomee.jpa.factory.lazy" value="true"/>
<property name="javax.persistence.jdbc.driver" value="org.postgresql.Driver"/>
<property name="javax.persistence.jdbc.url" value="jdbc:postgresql://localhost/click10"/>
<property name="javax.persistence.jdbc.user" value="postgres"/>
<property name="javax.persistence.jdbc.password" value="postgres"/>
<property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQL82Dialect"/>
<property name="hibernate.show_sql" value="true"/>
<property name="hibernate.format_sql" value="true"/>
<property name="hibernate.hbm2ddl.auto" value="update"/>
</properties>
</persistence-unit>
我已成功使用 TomEE 嵌入式容器进行单元测试。与任何外部 JUnit 资源一样,它可以使用 @Rule
进行管理,因此我有两个 classes,规则 class 和嵌入式 TomEE 的包装器。
TomEE Container
class 的包装器 class。配置嵌入式 derby 数据源和用户,以便我们可以测试基本身份验证。
/**
* class for starting an Embedded TomEE server which will scan the classpath and start the application.
* The configuration configures an InMemory derby database, and tells JPA to create tables based on the Entity annotations
*
*/
public class EmbeddedTomEE {
public static final String USERNAME = "aUser";
public static final String PASSWORD = "aPassword";
private Container container;
public void start() {
Configuration configuration = new Configuration();
Properties properties = new Properties();
properties.setProperty("jdbc/UdDB", "new://Resource?type=DataSource");
properties.setProperty("jdbc/UdDB.jdbcDriver", "org.apache.derby.jdbc.EmbeddedDriver");
properties.setProperty("jdbc/UdDB.jdbcUrl", "jdbc:derby:memory:udb;create=true");
properties.setProperty("jdbc/UdDB.username", "SA");
properties.setProperty("jdbc/UdDB.password", "");
properties.setProperty("jdbc/UdDB.jtaManaged", "true");
properties.setProperty("persistence_unit.javax.persistence.schema-generation.database.action", "create");
properties.setProperty("persistence_unit.javax.persistence.sql-load-script-source", "META-INF/testdata.sql");
properties.setProperty("rest-persistence_unit.eclipselink.logging.level", "FINE"); //use 'FINE' for JPA logging
configuration.setProperties(properties);
// use a random port so we can have TomEE running parallel with tests
configuration.randomHttpPort();
configuration.setWebXml("src/main/webapp/WEB-INF/web.xml");
HashMap<String, String> users = new HashMap<>();
users.put(USERNAME, PASSWORD);
configuration.setUsers(users);
HashMap<String, String> roles = new HashMap<>();
roles.put("aUser", "user");
configuration.setRoles(roles);
container = new Container(configuration).deployClasspathAsWebApp();
}
public int getPort() {
return container.getConfiguration().getHttpPort();
}
public void stop() {
container.close();
}
}
负责在执行每个测试之前启动嵌入式 TomEE 的 JUnit 规则。我们还有一些逻辑可以避免在每次测试时启动和停止容器的成本。 class 还创建了一个 JAX-RS webClient,可用于调用应用程序 REST 服务。
/**
* JUnit rule for running an EmbeddedTomEE in memory. The rule has static state, this is to avoid starting and stopping the embedded container
* with every test. Every time no test are running we start a timer, which is canceled if another test is started. This way the rule works well for a
* single test run inside an IDE, and running multiple tests from Maven.
*
*/
public class EmbeddedTomEERule extends ExternalResource {
private static EmbeddedTomEE tomEE;
private static final AtomicInteger count = new AtomicInteger();
private static Timer timer;
@Override
protected void before() throws Throwable {
startIfNeeded();
if (timer != null) {
timer.cancel();
}
count.incrementAndGet();
}
@Synchronized
private void startIfNeeded() {
if (tomEE == null) {
tomEE = new EmbeddedTomEE();
tomEE.start();
Runtime.getRuntime().removeShutdownHook(new Thread(() -> tomEE.stop()));
}
}
@Override
protected void after() {
int runningTests = count.decrementAndGet();
if (runningTests == 0) {
// stop after some time if no new test are started
timer = new Timer();
timer.schedule(new StopEmbeddedContainer(), 10000);
}
}
public int getPort() {
return tomEE.getPort();
}
/**
* creates a new WebClient that can request data from the specified path
*/
public WebClient getWebClient(String path, MediaType mediatype) {
WebClient client = WebClient.create("http://localhost:" + tomEE.getPort() + "/", Collections.singletonList(new JohnzonProvider()),
EmbeddedTomEE.USERNAME, EmbeddedTomEE.PASSWORD, null)
.path(path).accept(mediatype);
return client;
}
private static class StopEmbeddedContainer extends TimerTask {
@Override
public void run() {
tomEE.stop();
}
}
}
这是一个测试的示例
public class ExampleTest {
@Rule
public EmbeddedTomEERule rule = new EmbeddedTomEERule();
@Test
public void doTest() {
WebClient client = rule.getWebClient("some-endpoint", MediaType.APPLICATION_JSON_TYPE);
Output dto = client.get(Input.class);
}
}
这种类型的测试允许您在 HTTP 层测试您的应用程序,并且允许您在测试和服务器代码中放置断点。从技术上讲,将其称为单元测试可能有点牵强,但在测试多个组件时,我更喜欢这种类型的测试。由于您需要一个功能齐全的 TomEE,因此您需要提供一些外部依赖项,在我的例子中它看起来像这样:
<dependency>
<groupId>org.apache.derby</groupId>
<artifactId>derby</artifactId>
<version>${derby.db.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.tomee</groupId>
<artifactId>openejb-core</artifactId>
<version>${openejb-core.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.tomee</groupId>
<artifactId>openejb-cxf-rs</artifactId>
<version>${openejb-core.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.tomee</groupId>
<artifactId>openejb-server</artifactId>
<version>${openejb-core.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.tomee</groupId>
<artifactId>openejb-rest</artifactId>
<version>${openejb-core.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.tomee</groupId>
<artifactId>tomee-embedded</artifactId>
<version>${openejb-core.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.glassfish.web</groupId>
<artifactId>el-impl</artifactId>
<version>2.2</version>
<scope>test</scope>
</dependency>