2017-09-10 72 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>