# Mit welchen Tools EJBs testen?



## JUnkie (29. Mai 2007)

Hi,

was benutzt Ihr für Unit- und/oder Integrationstests von EJB Session und MD Beans (insbesondere EJB 3.0)?

MockEJB und Apache Cactus scheinen sich nur auf EJB 2.1 zu beziehen. Bei JUnit (junit.framework.ejb.ServerTestCase) bin ich mir nicht sicher.

Was empfehlt Ihr?

Danke!

Gruß
JUnkie


----------



## JUnkie (30. Mai 2007)

Gibt's hier denn niemanden, der seine EJBs testet?


----------



## boxi (31. Mai 2007)

Hallo

Ich hab noch nicht die riesen Erfahrung... aber ich mach das mit JUnit. Das funktioniert ganz gut.


----------



## JUnkie (1. Jun 2007)

Hey Boxi,

danke für Deine Antwort - machst Du das mit junit.framework.ejb.ServerTestCase?

Viele Grüße


----------



## semi (2. Jun 2007)

Ich gehe mal davon aus, dass du JPA verwendest. Persistence Injection funktioniert bei JUnit-Tests nicht,
daher muss man den EntityManager selbst erzeugen bzw. eine getrennte persistence.xml, speziell für Tests,
erzeugen und diese in Tests verwenden. Am besten erstelle ein getrenntes Projekt, in dem nur JUnit-Tests
enthalten sind.

Hier ein Beispiel (Hibernate als JPA Implementierung)

persistence.xml für den Serverbetrieb (DS über JNDI)
	
	
	
	





```
<persistence-unit name="OraclePU">
   <jta-data-source>java:oracle_ds</jta-data-source>

   <properties>
      <property name="hibernate.archive.autodetection" value="class" />
      <property name="hibernate.dialect" value="org.hibernate.dialect.Oracle9Dialect" />
   ...
```
persistence.xml für Tests (kein DS über JNDI, dafür Driver,Url etc.)
	
	
	
	





```
<persistence-unit name="OraclePU">
   <provider>org.hibernate.ejb.HibernatePersistence</provider>
   <properties>
      <property name="hibernate.archive.autodetection" value="class" />
      <property name="hibernate.dialect" value="org.hibernate.dialect.Oracle9Dialect" />
      <property name="hibernate.connection.driver_class" value="oracle.jdbc.driver.OracleDriver" />
      <property name="hibernate.connection.url" value="jdbc:oracle:thin:@127.0.0.1:1521:XE" />
      <property name="hibernate.connection.username" value="username" />
      <property name="hibernate.connection.password" value="password" />
   ...
```
Deine Session Bean
	
	
	
	





```
@Stateless
@Local(...)
public class BankingFacade implements ...
{
   private static final long serialVersionUID = ...;

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

   public BankingFacade()
   {
   }

   /**
   * Setter für EntityManager. Diese Methode wird nur in der Testumgebung benötigt.
   *
   * @param entityManager EntityManager aus der Testumgebung
   */
   public void setEntityManager( EntityManager entityManager )
   {
      this.entityManager = entityManager;
   }

   ...
```
Abstrakte Implementierung einer Testklasse. Stellt EntityManager zur Verfügung
	
	
	
	





```
public abstract class AbstractTestCase
{
   private static EntityManager entityManager;

   @BeforeClass
   public static void setUp() throws Exception
   {
      EntityManagerFactory factory = Persistence.createEntityManagerFactory( "OraclePU" );
      entityManager = factory.createEntityManager();
   }

   @AfterClass
   public static void tearDown() throws Exception
   {
      if( entityManager != null )
      {
         entityManager.close();
      }
   }

   protected EntityManager getEntityManager()
   {
      return entityManager;
   }
}
```
Test für die BankingFacade
	
	
	
	





```
public class BankingFacadeTest extends AbstractTestCase
{
   private EntityTransaction transaction;

   @Before
   public void beforeTest()
   {
      transaction = getEntityManager().getTransaction();
      transaction.begin();
   }

   @After
   public void afterTest()
   {
      transaction.rollback();
   }

   @Test
   public void testWasAuchImmer()
   {
      BankingFacade facade = new BankingFacade();
      facade.setEntityManager(getEntityManager());

      facade...
   }

   ...

}
```


----------



## boxi (4. Jun 2007)

Was Semi geschrieben hat, tönt gut... ich mach es aber so, dass ich die JUnit als Client laufen lasse und so über RMI von aussen auf den Server zugreife... leider muss man heirführ wirklich eine Testinstance am laufen haben, was recht mühsam sein kann. Aber das Mit den EJBtest was du geschrieben hast mache ich nicht.

Grüsse Boxi


----------



## JUnkie (4. Jun 2007)

@ semi und boxi:

Danke für Eure guten Ideen! Wird morgen ausprobiert!

@ semi:

Das sieht ziemlich gut aus (bin gespannt auf das Ausprobieren) - bist Du da selbst drauf gekommen oder steht das in irgendeinem Buch?

@ boxi:

Auch interessant, aber das klappt doch nur für den Unit-Test der Methoden, die im Interface definiert sind, oder? Wenn ich auch noch eine kleine Helper-Methode unit-testen möchte, die nicht im Interface definiert ist, dann geht's nicht mit Deiner Idee, oder habe ich da etwas falsch verstanden?

Gruß


----------



## Guest (5. Jun 2007)

Das stimmt leider... soweit habe ich mich NOCH nicht eingearbeitet... kommt aber sicher noch  :wink:


----------



## semi (5. Jun 2007)

JUnkie hat gesagt.:
			
		

> Das sieht ziemlich gut aus (bin gespannt auf das Ausprobieren) - bist Du da selbst drauf gekommen oder steht das in irgendeinem Buch?


Kein Buch, aber wenn man weiss, wie die Dinger laufen, kann man eins und eins zusammenzählen.


----------



## JUnkie (5. Jun 2007)

@semi:

Ich finde das trotzdem ziemlich genial, zumal selbst im 500-Seiten-EJB3-Wälzer von Bill Burke nichts über das Testen steht. Man muss da erstmal drauf kommen.

Und andere JNDI-Ressourcen? Wie mockst Du die? Man kann doch eigentlich alles, was mit @EJB, @Ressource etc. annotiert wird, ebenfalls über den Aufruf von Settern aus der JUnit-Klasse heraus lösen, oder?


----------



## semi (5. Jun 2007)

JUnkie hat gesagt.:
			
		

> Und andere JNDI-Ressourcen? Wie mockst Du die? Man kann doch eigentlich alles, was mit @EJB, @Ressource etc. annotiert wird, ebenfalls über den Aufruf von Settern aus der JUnit-Klasse heraus lösen, oder?


Sowas mache ich nicht. Ich gehe zunächst mal davon aus, dass diese Mechanismen funktionieren und nicht getestet 
werden müssen (Jede Katastrophe beginnt mit einer Vermutung ). Das ganze aus dem Grund, dass ich Facaden, 
Webservices etc. nie mit irgendwelcher Anwendungslogik versehe. Die Dinger sind einfache "Schleusen" für die 
dahinter liegende Geschäftslogik, die eine Schicht weiter implementiert ist. Eine Facade beschränkt sich nur auf die 
Weiterleitung des Aufrufs, Exception-Logging, Exception-Handling (z.B. Konvertieren serverseitiger Exceptions in 
eine allgemeine Exception, nennen wir sie mal ServerException, die bis zum Client weitergereicht wird und nur ein 
Minimum an Informationen enthält (Errorcode und übersetzte Fehlermeldung).

Aber wenn ich es machen würde, dann vermutlich ähnlich wie mit der Facade (einfach einen Setter für die anderen
Ressourcen/EJBs etc.)

Alternativ auf die Ressourcen/EJBs über Getter zugreifen und diesen in einem Mock-Objekt überschreiben
	
	
	
	





```
public class BankingFacade ...
{
   @EJB
   private CustomerFacade customerFacade; // Eigentlich auch gaga. Facade in Facade ist etwas uncool.

   ... Constructore etc.

   protected CustomerFacade getCustomerFacade()
   {
      return customerFacade;
   }


   public void tueWasAuchImmer()
   {
      ...
      // Nie direkt auf customerFacade zugreifen, sondern immer über die Getter-Methode
      getCustomerFacade().tueWasAuchImmer();
      ...
   }
}

public BankingFacadeMock extends BankingFacade implements ...
{
   private CustomerFacade customerFacade;

   protected CustomerFacade getCustomerFacade()
   {
      if(customerFacade == null)
      {
         customerFacade = new CustomerFacadeMock();
         customerFacade.setEntityManager(getEntityManager());
      }
      return customerFacade;
   }
}
```
Nachteil der zweiten Methode ist, dass man die ganzen Mock-Klassen implementieren muss.
Viel Getippe ohne irgendeinen erkennbaren Vorteil.


----------



## JUnkie (5. Jun 2007)

Okay... dann muss ich nun erstmal ausprobieren, ob der in die SessionBeans injizierte EntityManager auch in "normalen" Klassen im AppServer funktioniert, also ob man aus der SB "normaleKlasse.tuEtwas(entityManager)" aufrufen kann. Da bin ich mir nicht sicher...

Hätte noch einen kleinen Verbesserungsvorschlag für Deinen coolen AbstractTestCase. Habe in meine persistence.xml das automatische Generieren und wieder Aufräumen des DB-Schemas aufgenommen. Das Aufräumen klappt aber nur, wenn nicht nur der EntityManager, sondern auch die EntityManagerFactory am Ende geschlossen wird (was darauf hindeutet, dass man ansonsten etwas unsauber aus dem TestCase geht :wink:  ):


```
public abstract class AbstractTestCase 
{ 
   private static EntityManager entityManager; 
   private static EntityManagerFactory factory;

   @BeforeClass 
   public static void setUp() throws Exception 
   { 
      factory = Persistence.createEntityManagerFactory("UnitName"); 
      entityManager = factory.createEntityManager(); 
   } 

   @AfterClass 
   public static void tearDown() throws Exception 
   { 
      if( entityManager != null ) 
      { 
         entityManager.close(); 
      } 
      if( factory != null ) 
      { 
      	factory.close(); 
      } 
   } 

   ...
}
```


----------



## semi (5. Jun 2007)

:toll: Stimmt, sorry, das Ding gehört auch noch geschlossen. Spätestens, wenn man es laufen lässt, merkt man es.


----------

