With EJB3Unit you can create and test entity beans outside the container. EJB3Unit will automate your entity bean testing. Ejb3Unit will generate random (or customized) entity beans for you and test the read write access against your database. During this test possible data truncations, wrong declared nullable fields or other possible schema errors will be checked. Furthermore EJB3Unit will check your equals and hashCode implementations for your Entity beans.
Sometimes Ejb3Unit your use case will be too complex for automated testing. In this case you should read the section testing complex object graphs!
How to write a simple Entity bean test?
We assume that a Book bean is an entity bean
@Entity @Table(name = "AUTOR") public class Author implements Serializable{ @Id(generate = GeneratorType.NONE) private int id; private String name; @Column(name="creation_timestamp", nullable=false) private Date created; }
If we want to write a JUnit test for this entity bean we write the following piece of code:
public class Author Test extends BaseEntityTest<Author> { /** * Constructor. */ public StockWKNBoTest() { super(Author.class); } }
But what will happen behind the facade?
Ejb3Unit will start to analyse the meta data information of the entity bean. This means: What are the persistent fields, what the primary key fields, which fields are transient and so on?
Using all this Meta-Informations Ejb3Unit is able to generate random instances of the Author bean.
Now Ejb3Unit will make very useful tests automatically for you. The current Ejb3Unit Implementation will:
Later we will see how to make a Parameterisation of the entity instance generation.
In many cases the entity beans which are intended to test have relations. Possible relations are OneToMany, ManyToOne and OneToOne.
With Ejb3Unit its possible to Test this beans AND the relations (in a non transitive way!).
Testing beans with OneToMany relations
The first thing you have to write is a generator which generates n bans for the Collection filed (representing the n side)
In this example we ere going to create a Order. Typically a Order has relation to n LineItems. The LineItems are represented by the property lineItems which is of type Collection.
As the first step we must create a (inner) Class a Generator, called MyLineItemCreator.
@GeneratorType(className = Collection.class,field="lineItems") class MyLineItemCreator extends BeanCollectionGenerator<LineItem> { private MyLineItemCreator() { super(LineItem.class, 10); } }
We simple inherit form the BeanCollectionGenerator. In the constructor we have to pass the amount of line items generated for each Order.
Now we can create an EntityTest and add this generator to this entity test:
public class OrderTest extends BaseEntityTest<Order> { private static final Generator[] SPECIAL_GENERATORS = { new MyLineItemCreator() }; public OrderTest() { super(Order.class, SPECIAL_GENERATORS); } }
Thats it! This test will always create Orders with 10 LineItem´s. Every LineItem will have an automatic back association to the Order (and vice versa)
In the previous test we generated only random entity beans instances. No we will show how the automatic generation of entity beans can be parameterized.
In the default case, every persistent field of an entity bean will be filled with random data. This default behaviour can be overwritten by using an own Generator implementation.
Imagine you would like to test the following entity bean:
@Entity @Table(name = "ARTICLE") public class Article implements Serializable{ @Id (generate = GeneratorType.TABLE) @Column (name = "id") private int id; private String title; private int autorId; }
We will assume that the autorId is a foreign key field to the Authors table (and we will ignore the fact, that such a realization of the entity bean is strange because we would normally use a relation to Author)
Now we would like to crate an entity bean test which will generate random data for the persistent fields id and title but the field autorId should have always the value 1 (because we know than in our database an Author with the primary key 1 exists).
To parameterize such an Ejb3Unit Test we need two things:
The generic generator interface is very simple:
public interface Generator<T> { T getValue(); }
The getValue() method generates a value for a distinct type T for a specified field. The Introspector class contains information about the Meta data of the bean.
Every concrete generator must contain Meta data information (Java 5 annotations) describing for which case the generator should be used. For example we could use following annotation:
@GeneratorType( className = Integer.class, field = "autorId", fieldType = FieldType.ALL_TYPES)
To describe that this generator should be used for all fields named autorId of type Integer.
With this knowledge we are now able to develop our custom generator:
@GeneratorType(className = Integer.class, field = "autorId", fieldType = FieldType.ALL_TYPES) public class ConstantIntegerGenerator implements Generator<Integer> { private final int constant; public ConstantIntegerGenerator(int constant) { this.constant = constant; } public Integer getValue() { return this.constant; } }
Thats it. Now we have to register our custom generator to our Ejb3Unit entity test:
public class ArticleTest extends BaseEntityTest<Article> { private static final Generator[] SPECIAL_GENERATORS = { new ConstantIntegerGenerator(1) }; public ArticleTest () { super(Article.class, SPECIAL_GENERATORS); } }
Thats it. Now all generated Articles will have the autorId=1 value and the bean can be persisted in the database.
But how Ejb3Unit knows which generator should be used for a concrete persistent field?
The answer is: Ejb3Unit use a well defined hierarchy of generators. Always the generator with the highest specialization will be used.
In our example the ConstantIntegerGenerator has a higher specialization as the build in RandomIntegerGenerator! This is the reason why our implementation is used.
The hierarchy is a follows:
As you see a linear hierarchy definition is used. Every concrete generator has a clear hierarchy level.
As explained before, every generator must implement the generator Interface
The generic generator interface is very simple:
public interface Generator<T> { T getValue(); }
If a generator needs different kinds of references, for the generation of the next value, annotations for dependency injection can be used.
Following annotations for dependency injection are possible inside the generator class:
Sometimes a generator need to prepare some things before a JunitTest is executedt and cleanup later his stuff when the JUnit test is completed. For this Ejb3Unit provides two annotations for life cycle methods:
Example: The following snippet demonstrated the usage of such Annotation:
@PrepareGenerator public void preCreate() { if (emf==null){ this.initEntityManagerFactory(); } }
The next code snippet shows a custom generator for Date generation.
@GeneratorType(className = Date.class, fieldType = FieldType.ALL_TYPES) public class RandomDateGenerator extends BaseUniqueValueGenerator<Date> implements Generator<Date> { @ForProperty private Property forProperty; @UsedIntrospector private Introspector introspector; public Date getValue() { return this.getUniqueValueForEachPkField(forProperty, introspector); } @Override protected Date generateCadidate() { return BaseRandomDataGenerator.getValueDate(); } }