2017-09-10 191 views
0

我使用TomEE和Intellij来测试我的EJB/JPA bean。我在this answer上看到我应该使用嵌入式容器进行测试。我在this other answer(来自同一个问题)上发现了Arquillian,但正如评论中所述,很难设置它,并且不是用户友好的,像我这样的初学者正在寻找。如何使用TomEE测试EJB?

不幸的是,我没有使用glassfish-embedded-all依赖项作为回答,但是tomee-embedded。我在official tutorial上看到它应该使用JTA以及上面的答案。但为什么?

否则作为最后一个环节,我正在此错误:

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:与使用TomEE嵌入容器单元测试

<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> 

回答

1

我已经成功。与任何外部JUnit资源一样,可以使用@Rule来管理它,所以我有两个类,即Rule类和Embedded TomEE的包装器。

TomEE Container类的包装类。配置嵌入式德比数据源和用户,以便我们可以测试基本身份验证。

/** 
* 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(); 
    } 
} 

JUnit Rule在每次测试执行之前负责启动嵌入式TomEE。我们也有一些逻辑来避免每次测试开始和停止容器的成本。该类还创建可用于调用应用程序REST服务的JAX-RS webClient。

/** 
* 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>